From c4696a8a6f171b5c865e4b407fe69fbfd2542784 Mon Sep 17 00:00:00 2001 From: Jakub Kida Date: Fri, 26 Jun 2026 11:47:49 +0200 Subject: [PATCH] fix: support mid-migration metadata temolates --- src/api/Metadata.js | 44 ++++++++++++++++++++++++++---------- src/common/types/metadata.js | 6 +++-- 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/src/api/Metadata.js b/src/api/Metadata.js index fb1153b780..9efcbb63cc 100644 --- a/src/api/Metadata.js +++ b/src/api/Metadata.js @@ -193,13 +193,20 @@ class Metadata extends File { } /** - * API URL for getting metadata template schema by template key + * API URL for getting metadata template schema by template key. + * + * In SCOPED mode the path segment is the `enterprise` shorthand. + * In MIGRATION/FINAL mode the API requires the full scope value (e.g. + * `enterprise_123456`) or a namespace FQN, so callers should pass the + * resolved scope/namespace when operating in those modes. * * @param {string} templateKey - metadata template key + * @param {string} [scope] - scope or namespace FQN; defaults to the + * `enterprise` shorthand for backward compatibility with SCOPED mode * @return {string} API url for getting template schema by template key */ - getMetadataTemplateSchemaUrl(templateKey: string): string { - return `${this.getMetadataTemplateUrl()}/enterprise/${templateKey}/schema`; + getMetadataTemplateSchemaUrl(templateKey: string, scope?: string = METADATA_SCOPE_ENTERPRISE): string { + return `${this.getMetadataTemplateUrl()}/${scope}/${templateKey}/schema`; } /** @@ -403,25 +410,27 @@ class Metadata extends File { } /** - * Gets metadata template schema by template key + * Gets metadata template schema by template key. + * + * In MIGRATION/FINAL mode, pass the full scope (e.g. `enterprise_123456`) + * or namespace FQN as `scope` so the correct URL path segment is used. + * Omitting `scope` falls back to the `enterprise` shorthand (SCOPED mode). * * @param {string} templateKey - template key + * @param {string} [scope] - scope or namespace FQN (defaults to `enterprise`) * @return {Promise} Promise object of metadata template */ - async getSchemaByTemplateKey(templateKey: string): Promise { + async getSchemaByTemplateKey(templateKey: string, scope?: string): Promise { const cache: APICache = this.getCache(); const key = this.getMetadataTemplateSchemaCacheKey(templateKey); - // Return cached value if it exists if (cache.has(key)) { return cache.get(key); } - // Fetch from API if not cached - const url = this.getMetadataTemplateSchemaUrl(templateKey); + const url = this.getMetadataTemplateSchemaUrl(templateKey, scope); const response = await this.xhr.get({ url }); - // Cache the response cache.set(key, response); return response; @@ -553,10 +562,21 @@ class Metadata extends File { const instanceId = instance.$id; const templateKey = instance.$template; const scope = instance.$scope; - const template = templates.find(t => t.templateKey === templateKey && t.scope === scope); + const namespace = instance.$namespace; + + // Primary match: by scope (SCOPED mode; also works for enterprise-scoped + // instances in MIGRATION mode where $scope is still populated). + let template = templates.find(t => t.templateKey === templateKey && t.scope === scope); + + // Fallback match: by namespace for namespace-only instances in + // MIGRATION/FINAL mode where $scope is absent. + if (!template && namespace) { + template = templates.find(t => t.templateKey === templateKey && t.namespace === namespace); + } - // Enterprise scopes are always enterprise_XXXXX - if (!template && scope.startsWith(METADATA_SCOPE_ENTERPRISE)) { + // Enterprise scopes are always enterprise_XXXXX; use optional chaining + // to guard against namespace-only instances where $scope is undefined. + if (!template && scope?.startsWith(METADATA_SCOPE_ENTERPRISE)) { // Any missing template is likely from another enterprise (e.g. collaborated file); // Templates array has no pagination so we can assume cross-enterprise as it contains all templates. const crossEnterpriseTemplates = await this.getTemplates(id, scope, instanceId, true); diff --git a/src/common/types/metadata.js b/src/common/types/metadata.js index ba99410e39..71646e2a90 100644 --- a/src/common/types/metadata.js +++ b/src/common/types/metadata.js @@ -49,7 +49,8 @@ type MetadataTemplate = { hidden?: boolean, id: string, isHidden?: boolean, - scope: string, // V2 + namespace?: string, // MIGRATION/FINAL modes + scope: string, // V2; absent for namespace-only templates in FINAL mode templateKey: string, // V3 }; @@ -115,8 +116,9 @@ type MetadataInstance = { type MetadataInstanceV2 = { $canEdit: boolean, $id: string, + $namespace?: string, // MIGRATION/FINAL modes $parent: string, - $scope: string, + $scope?: string, // absent for namespace-only instances in MIGRATION/FINAL modes $template: string, $type: string, $typeVersion: number,