L2.1 : résout entity_type via l'API Metadata (D21)

Context.{entity_type} attend le TableName du Metadata, pas le nom
d'entité de l'AD (Container -> Containers, mais Alias -> Alias) : un nom
faux partait en HTTP 500 de compilation LINQ. Nouveau service
entity-resolver.js : table Name|TableName (insensible à la casse) ->
TableName, agrégée sur les applications déployées (via
GET /configuration/applications — les applications sans contexte
requêtable n'y figurent pas et n'apportent 0 entité Metadata), cache TTL
partagé, invalidation par onSwitch (D8). Branché dans wms-query-service
(query/count/schema/search) et call_query_api.

Nom inconnu -> échec avant tout appel réseau de requête, suggestions
proches + renvoi vers get_entity_metadata. Metadata injoignable -> le
nom passe tel quel avec un warning dans la réponse.

Mesures (LIMAGRAIN, via le protocole) :
- query_wms_entities("Container", limit 1) -> succès, 1 ligne, résolu
  Containers
- query_wms_entities("Alias") -> succès, invariant (pas de pluriel)
- query_wms_entities("Item") -> "Item" n'existe pas dans le modèle
  Reading. Proches : RFMenuItems, Sites. 288 entités disponibles —
  aucune ligne [API] POST dans stderr
- count_wms_entities("Product") -> 51160
- get_entity_schema("Container") et call_query_api("Container") : mêmes
  résolutions
- 288 TableName distincts sur 5 applications, aucun conflit
  Name -> TableName (mesuré le 24/08/2026)

Docs : D21 dans DECISIONS.md ; CLAUDE.md (piège retiré des points
ouverts, liste d'entités corrigée Aliases -> Alias, entity-resolver dans
la structure) ; exemple singulier/pluriel dans wms://query-examples ;
ROADMAP allégée (cause racine + L2.1 + L3.3 livrés).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Arthur Ria
2026-08-24 17:28:27 +02:00
parent 8c5792da52
commit cb625a7918
8 changed files with 276 additions and 75 deletions
+15 -2
View File
@@ -4,6 +4,7 @@
*/
const apiService = require('./api-service').getInstance();
const entityResolver = require('./entity-resolver');
/**
* Build a LINQ select expression
@@ -20,13 +21,17 @@ const apiService = require('./api-service').getInstance();
* @param {number} limit - Result limit
*/
async function queryEntities(entityType, selectExpression = 'z => z', filter = null, limit = 100) {
// Résolution Name/TableName -> TableName (D21). Un nom inconnu échoue ici,
// avant tout appel réseau de requête — l'erreur porte les suggestions.
const { tableName, warning } = await entityResolver.resolveEntityType(entityType);
try {
// Enforce max limit
const maxLimit = parseInt(process.env.MAX_QUERY_ROWS) || 1000;
const actualLimit = Math.min(limit, maxLimit);
// Build expression: Context + optional Where + OrderBy (required by EF when Take is used)
let expression = `Context.${entityType}`;
let expression = `Context.${tableName}`;
if (filter) {
const whereExpr = /^\s*\w+\s*=>/.test(filter) ? filter : `z => ${filter}`;
expression += `.Where(${whereExpr})`;
@@ -43,6 +48,8 @@ async function queryEntities(entityType, selectExpression = 'z => z', filter = n
return {
entityType,
resolvedTableName: tableName,
...(warning ? { warning } : {}),
expression,
limit: actualLimit,
count: Array.isArray(result) ? result.length : 0,
@@ -144,9 +151,13 @@ async function getEntitySchema(entityType) {
* @param {string|null} filter - Optional filter
*/
async function countEntities(entityType, filter = null) {
// Résolution Name/TableName -> TableName (D21) — échec avant appel réseau
// sur nom inconnu.
const { tableName, warning } = await entityResolver.resolveEntityType(entityType);
try {
// Build: Context.Entity.Where(...).Count()
const parts = [`Context.${entityType}`];
const parts = [`Context.${tableName}`];
if (filter) {
const whereExpr = /^\s*\w+\s*=>/.test(filter) ? filter : `z => ${filter}`;
parts.push(`Where(${whereExpr})`);
@@ -160,6 +171,8 @@ async function countEntities(entityType, filter = null) {
return {
entityType,
resolvedTableName: tableName,
...(warning ? { warning } : {}),
filter,
count
};