fix(guardian-service): clone a template-bound policy whole, or not at all - #6698
fix(guardian-service): clone a template-bound policy whole, or not at all#6698vshvets-bc wants to merge 2 commits into
Conversation
… all Two halves of the same defect. resolveSchemaTemplate only tried metadata.schemaTemplate.templateId and a message id, and clonePolicy passes no metadata - so cloning a policy bound to an unpublished template threw "Selected schema template is inaccessible" even though the template was sitting right there in the database. It now falls back to the binding's own templateId, under the same accessibility rule as the metadata branch: published, or owned by the caller. The other half is the half-bound clone. saveSchemaTemplateSnapshot nulls policy.schemaTemplate when there is no snapshot to remap - exactly what a clone produces - but it runs after the schemas are persisted, so they kept their templateId, templateSchemaId and templateFieldId markers: a policy claiming no template over schemas still claiming one. The decision now happens up front, beside the explicit detach and before the schemas are written, which is what makes the strip persist. A binding with no snapshot to carry is dropped whole. That decision is extracted as mustDropSchemaTemplateBinding so it can be tested without standing up the whole import pipeline; clearTemplateMetadataFromSchemas loses its `private` for the same reason. Tests: 9 cases in guardian-service/tests/unit/policy-import-schema-template-binding.test.mjs, 6 of which fail against the previous implementation. Signed-off-by: Volodymyr Shvets <Volodymyr.Shvets@envisionblockchain.com>
Kept the mechanism, dropped the retelling of the symptom - that belongs in the commit message. Signed-off-by: Volodymyr Shvets <volodymyr.shvets@climission.com>
|
|
||
| // clonePolicy passes no metadata, so an unpublished template was unreachable | ||
| // even though it sits in the database. Same rule: published, or owned. | ||
| if (binding.templateId) { |
There was a problem hiding this comment.
The binding fallback is unreachable on the clone path it cites.
import() evaluates mustDropSchemaTemplateBinding at line 931, before resolveSchemaTemplate at 993. clonePolicy at guardian-service/src/policy-engine/policy-engine.ts:798 builds dataToCreate with no schemaTemplateSnapshot, so for a bound policy mustDrop is true, policy.schemaTemplate is nulled at 932, and resolveSchemaTemplate returns at 617–620 on !binding. The new block never runs on a clone – the fix for the "inaccessible" throw is the drop, not the fallback. The three resolveSchemaTemplate tests pass only because they call the method directly with a still-bound policy, bypassing that ordering. Either remove the branch and its comment, or (if a clone should stay bound to its unpublished template) run resolveSchemaTemplate first and take the drop decision from this.schemaTemplate.
Two halves of the same defect: cloning a policy that has a schema-template binding either hard-fails, or succeeds and produces a half-bound clone.
1. Cloning a policy bound to an unpublished template throws
resolveSchemaTemplateonly triesmetadata.schemaTemplate.templateIdand a message id:clonePolicypasses no metadata, and an unpublished template has notemplateMessageId— so the clone fails withSelected schema template is inaccessible, even though the template is sitting right there in the database and the caller owns it.It now falls back to the binding's own
templateId, under the same accessibility rule the metadata branch already applies: published, or owned by the caller. An inaccessible template still falls through to the message-id path and the existing error.2. The half-bound clone
saveSchemaTemplateSnapshotnullspolicy.schemaTemplatewhen there is no snapshot to remap — which is exactly whatclonePolicyproduces. But it runs after the schemas are persisted, so they keep theirtemplateId,templateSchemaIdandtemplateFieldIdmarkers.The result is a policy claiming no template, sitting over schemas that still claim one. Any subsequent template operation then sees an inconsistent pair.
The decision moves to the top of the import, next to the explicit detach and before the schemas are written — which is what makes the marker strip persist:
A binding with no snapshot to carry is dropped whole rather than half.
Testability note
mustDropSchemaTemplateBindingis extracted so the decision can be tested without standing up the entire import pipeline, andclearTemplateMetadataFromSchemasloses itsprivatefor the same reason. Neither changes behaviour.Tests
guardian-service/tests/unit/policy-import-schema-template-binding.test.mjs— 9 cases, 6 fail againstdevelop:guardian-service unit suite: 1798 passing.
🤖 Generated with Claude Code