@@ -43,6 +43,54 @@ function fixSchOwnNmAr(a_sch) {
4343 }
4444}
4545
46+ /**
47+ * Validates and parses a schema ID string. Returns the bare ID and version number.
48+ * If no version suffix is present, version is null.
49+ *
50+ * @param {string } schId - Schema ID, optionally with ":version" suffix
51+ * @returns {{ id: string, ver: number|null } }
52+ */
53+ function parseSchemaId ( schId ) {
54+ const colonCount = ( schId . match ( / : / g) || [ ] ) . length ;
55+
56+ if ( colonCount > 1 ) {
57+ throw [ error . ERR_INVALID_PARAM , "Schema ID contains multiple colons: '" + schId + "'" ] ;
58+ }
59+
60+ if ( colonCount === 0 ) {
61+ return { id : schId , ver : null } ;
62+ }
63+
64+ const idx = schId . indexOf ( ":" ) ;
65+ const verStr = schId . substr ( idx + 1 ) ;
66+ const ver = Number ( verStr ) ;
67+
68+ if ( verStr . length === 0 ) {
69+ throw [ error . ERR_INVALID_PARAM , "Schema ID has trailing colon with no version: '" + schId + "'" ] ;
70+ }
71+
72+ if ( ! Number . isInteger ( ver ) ) {
73+ throw [ error . ERR_INVALID_PARAM , "Schema ID version suffix is not a valid integer: '" + verStr + "'" ] ;
74+ }
75+
76+ return { id : schId . substr ( 0 , idx ) , ver : ver } ;
77+ }
78+
79+ /**
80+ * Strips version suffix from a schema ID field if present.
81+ * Handles the case where procInputParam composites "id:ver" into obj.id.
82+ *
83+ * @param {object } obj - Object with an id field to clean
84+ */
85+ function stripSchemaIdVersion ( obj ) {
86+ if ( obj . id ) {
87+ var idx = obj . id . indexOf ( ":" ) ;
88+ if ( idx >= 0 ) {
89+ obj . id = obj . id . substr ( 0 , idx ) ;
90+ }
91+ }
92+ }
93+
4694// Find all references (internal and external), load them, then place in refs param (object)
4795// This allows preloading schema dependencies for schema processing on client side
4896function _resolveDeps ( a_sch_id , a_refs ) {
@@ -123,9 +171,18 @@ router
123171 obj . own_nm = client . name ;
124172 }
125173
174+ const parsed = parseSchemaId ( req . body . id ) ;
175+
176+ if ( parsed . ver !== null && parsed . ver !== 0 ) {
177+ throw [ error . ERR_INVALID_PARAM , "Schema ID version must be 0 for creation, got: " + parsed . ver ] ;
178+ }
179+
126180 g_lib . procInputParam ( req . body , "_sch_id" , false , obj ) ;
127181 g_lib . procInputParam ( req . body , "desc" , false , obj ) ;
128182
183+ // Strip version suffix that procInputParam composited into obj.id
184+ stripSchemaIdVersion ( obj ) ;
185+
129186 sch = g_db . sch . save ( obj , {
130187 returnNew : true ,
131188 } ) . new ;
@@ -210,16 +267,12 @@ router
210267 waitForSync : true ,
211268 action : function ( ) {
212269 const client = g_lib . getUserFromClientID ( req . queryParams . client ) ;
213- var idx = req . queryParams . id . indexOf ( ":" ) ;
214- if ( idx < 0 ) {
270+
271+ const parsed = parseSchemaId ( req . queryParams . id ) ;
272+ if ( parsed . ver === null ) {
215273 throw [ error . ERR_INVALID_PARAM , "Schema ID missing version number suffix." ] ;
216274 }
217- var sch_id = req . queryParams . id . substr ( 0 , idx ) ,
218- sch_ver = parseInt ( req . queryParams . id . substr ( idx + 1 ) ) ,
219- sch_old = g_db . sch . firstExample ( {
220- id : sch_id ,
221- ver : sch_ver ,
222- } ) ;
275+ let sch_old = g_db . sch . firstExample ( { id : parsed . id , ver : parsed . ver } ) ;
223276
224277 if ( ! sch_old ) {
225278 throw [
@@ -272,6 +325,7 @@ router
272325 }
273326
274327 g_lib . procInputParam ( req . body , "_sch_id" , true , obj ) ;
328+ stripSchemaIdVersion ( obj ) ;
275329
276330 if (
277331 obj . id &&
@@ -382,16 +436,11 @@ router
382436 waitForSync : true ,
383437 action : function ( ) {
384438 const client = g_lib . getUserFromClientID ( req . queryParams . client ) ;
385- var idx = req . queryParams . id . indexOf ( ":" ) ;
386- if ( idx < 0 ) {
439+ const parsed = parseSchemaId ( req . queryParams . id ) ;
440+ if ( parsed . ver === null ) {
387441 throw [ error . ERR_INVALID_PARAM , "Schema ID missing version number suffix." ] ;
388442 }
389- var sch_id = req . queryParams . id . substr ( 0 , idx ) ,
390- sch_ver = parseInt ( req . queryParams . id . substr ( idx + 1 ) ) ,
391- sch = g_db . sch . firstExample ( {
392- id : sch_id ,
393- ver : sch_ver ,
394- } ) ;
443+ let sch = g_db . sch . firstExample ( { id : parsed . id , ver : parsed . ver } ) ;
395444
396445 if ( ! sch )
397446 throw [
@@ -540,57 +589,64 @@ router
540589 description : `Delete schema. Schema ID: ${ req . queryParams . id } ` ,
541590 } ) ;
542591
543- const client = g_lib . getUserFromClientID ( req . queryParams . client ) ;
544- var idx = req . queryParams . id . indexOf ( ":" ) ;
545- if ( idx < 0 ) {
546- throw [ error . ERR_INVALID_PARAM , "Schema ID missing version number suffix." ] ;
547- }
548- var sch_id = req . queryParams . id . substr ( 0 , idx ) ,
549- sch_ver = parseInt ( req . queryParams . id . substr ( idx + 1 ) ) ;
592+ g_db . _executeTransaction ( {
593+ collections : {
594+ read : [ "u" , "uuid" , "accn" , "sch_dep" , "sch_ver" ] ,
595+ write : [ "sch" ] ,
596+ } ,
597+ waitForSync : true ,
598+ action : function ( ) {
550599
551- sch_old = g_db . sch . firstExample ( {
552- id : sch_id ,
553- ver : sch_ver ,
554- } ) ;
600+ const client = g_lib . getUserFromClientID ( req . queryParams . client ) ;
601+ const parsed = parseSchemaId ( req . queryParams . id ) ;
602+ if ( parsed . ver === null ) {
603+ throw [ error . ERR_INVALID_PARAM , "Schema ID missing version number suffix." ] ;
604+ }
605+ sch_old = g_db . sch . firstExample ( { id : parsed . id , ver : parsed . ver } ) ;
555606
556- if ( ! sch_old )
557- throw [ error . ERR_NOT_FOUND , "Schema '" + req . queryParams . id + "' not found." ] ;
607+ if ( ! sch_old )
608+ throw [ error . ERR_NOT_FOUND , "Schema '" + req . queryParams . id + "' not found." ] ;
558609
559- if ( sch_old . own_id != client . _id && ! client . is_admin ) throw error . ERR_PERM_DENIED ;
610+ if ( sch_old . own_id != client . _id && ! client . is_admin ) throw error . ERR_PERM_DENIED ;
560611
561- // Cannot delete schemas that are in use
562- if ( sch_old . cnt ) {
563- throw [
564- error . ERR_PERM_DENIED ,
565- "Schema is associated with data records - cannot update ." ,
566- ] ;
567- }
612+ // Cannot delete schemas that are in use
613+ if ( sch_old . cnt ) {
614+ throw [
615+ error . ERR_PERM_DENIED ,
616+ "Schema is associated with data records - cannot delete ." ,
617+ ] ;
618+ }
568619
569- // Cannot delete schemas references by other schemas
570- if (
571- g_db . sch_dep . firstExample ( {
572- _to : sch_old . _id ,
573- } )
574- ) {
575- throw [
576- error . ERR_PERM_DENIED ,
577- "Schema is referenced by another schema - cannot update ." ,
578- ] ;
579- }
620+ // Cannot delete schemas references by other schemas
621+ if (
622+ g_db . sch_dep . firstExample ( {
623+ _to : sch_old . _id ,
624+ } )
625+ ) {
626+ throw [
627+ error . ERR_PERM_DENIED ,
628+ "Schema is referenced by another schema - cannot delete ." ,
629+ ] ;
630+ }
580631
581- // Only allow deletion of oldest and newest revisions of schemas
582- if (
583- g_db . sch_ver . firstExample ( {
584- _from : sch_old . _id ,
585- } ) &&
586- g_db . sch_ver . firstExample ( {
587- _to : sch_old . _id ,
588- } )
589- ) {
590- throw [ error . ERR_PERM_DENIED , "Cannot delete intermediate schema revisions." ] ;
591- }
632+ // Only allow deletion of oldest and newest revisions of schemas
633+ if (
634+ g_db . sch_ver . firstExample ( {
635+ _from : sch_old . _id ,
636+ } ) &&
637+ g_db . sch_ver . firstExample ( {
638+ _to : sch_old . _id ,
639+ } )
640+ ) {
641+ throw [ error . ERR_PERM_DENIED , "Cannot delete intermediate schema revisions." ] ;
642+ }
643+
644+ g_graph . sch . remove ( sch_old . _id ) ;
645+
646+ res . send ( ) ;
647+ } ,
648+ } ) ;
592649
593- g_graph . sch . remove ( sch_old . _id ) ;
594650 logger . logRequestSuccess ( {
595651 client : req . queryParams ?. client ,
596652 correlationId : req . headers [ "x-correlation-id" ] ,
@@ -632,16 +688,12 @@ router
632688 description : `View schema. Schema ID: ${ req . queryParams . id } ` ,
633689 } ) ;
634690 const client = g_lib . getUserFromClientID ( req . queryParams . client ) ;
635- var idx = req . queryParams . id . indexOf ( ":" ) ;
636- if ( idx < 0 ) {
691+
692+ const parsed = parseSchemaId ( req . queryParams . id ) ;
693+ if ( parsed . ver === null ) {
637694 throw [ error . ERR_INVALID_PARAM , "Schema ID missing version number suffix." ] ;
638695 }
639- var sch_id = req . queryParams . id . substr ( 0 , idx ) ,
640- sch_ver = parseInt ( req . queryParams . id . substr ( idx + 1 ) ) ;
641- sch = g_db . sch . firstExample ( {
642- id : sch_id ,
643- ver : sch_ver ,
644- } ) ;
696+ sch = g_db . sch . firstExample ( { id : parsed . id , ver : parsed . ver } ) ;
645697
646698 if ( ! sch ) throw [ error . ERR_NOT_FOUND , "Schema '" + req . queryParams . id + "' not found." ] ;
647699
0 commit comments