diff --git a/i18n/en.pot b/i18n/en.pot index 0a68ef12a..6d5fde1b5 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2026-04-20T04:35:46.003Z\n" -"PO-Revision-Date: 2026-04-20T04:35:46.003Z\n" +"POT-Creation-Date: 2026-04-27T07:43:21.475Z\n" +"PO-Revision-Date: 2026-04-27T07:43:21.476Z\n" msgid "" "THIS NEW RELEASE INCLUDES SHARING SETTINGS PER INSTANCES. FOR THIS VERSION " diff --git a/src/data/metadata/MetadataD2ApiRepository.ts b/src/data/metadata/MetadataD2ApiRepository.ts index df747e718..1b3732d94 100644 --- a/src/data/metadata/MetadataD2ApiRepository.ts +++ b/src/data/metadata/MetadataD2ApiRepository.ts @@ -68,14 +68,20 @@ export class MetadataD2ApiRepository implements MetadataRepository { public async getMetadataByIds( ids: string[], fields?: object | string, - includeDefaults = false + includeDefaults = false, + preserveNestedDefaultRefs = false ): Promise> { const { apiVersion } = this.targetInstance; const d2ApiDataStore = new D2ApiDataStore(this.targetInstance); const dataStoreIds = DataStoreMetadata.getDataStoreIds(ids); const requestFields = typeof fields === "object" ? getFieldsAsString(fields) : fields; - const d2Metadata = await this.getMetadata(ids, requestFields, includeDefaults); + const d2Metadata = await this.getMetadata( + ids, + requestFields, + includeDefaults, + preserveNestedDefaultRefs + ); if (apiVersion >= 32 && d2Metadata["dashboards"] && fields === undefined) { //Fix dashboard bug from 2.32 @@ -84,7 +90,8 @@ export class MetadataD2ApiRepository implements MetadataRepository { const fixedD2Metadata = await this.getMetadata( ids, ":all,dashboardItems[:all,visualization[id,type]]", - includeDefaults + includeDefaults, + preserveNestedDefaultRefs ); const metadataPackage = this.transformationRepository.mapPackageFrom( @@ -589,11 +596,13 @@ export class MetadataD2ApiRepository implements MetadataRepository { private async getMetadata( elements: string[], fields = ":all", - includeDefaults: boolean + includeDefaults: boolean, + preserveNestedDefaultRefs: boolean ): Promise> { try { const promises = []; const chunkSize = 50; + const keepDefaults = includeDefaults || preserveNestedDefaultRefs; for (let i = 0; i < elements.length; i += chunkSize) { const requestElements = elements.slice(i, i + chunkSize).toString(); @@ -602,7 +611,7 @@ export class MetadataD2ApiRepository implements MetadataRepository { .get("/metadata", { fields, filter: "id:in:[" + requestElements + "]", - defaults: includeDefaults ? undefined : "EXCLUDE", + defaults: keepDefaults ? undefined : "EXCLUDE", }) .getData() ); diff --git a/src/domain/metadata/builders/MetadataPayloadBuilder.ts b/src/domain/metadata/builders/MetadataPayloadBuilder.ts index 3ede28837..803cae0dd 100644 --- a/src/domain/metadata/builders/MetadataPayloadBuilder.ts +++ b/src/domain/metadata/builders/MetadataPayloadBuilder.ts @@ -238,7 +238,13 @@ export class MetadataPayloadBuilder { // Get all the required metadata const originInstance = await this.getOriginInstance(originInstanceId); const metadataRepository = this.repositoryFactory.metadataRepository(originInstance); - const syncMetadata = await metadataRepository.getMetadataByIds(newIds); + // DataSets need preserveNestedDefaultRefs because the server-side defaults=EXCLUDE + // strips the default categoryOptionCombo.id from compulsoryDataElementOperands, + // which then fails on import. Client-side default stripping still runs. + const syncMetadata = + type === "dataSets" + ? await metadataRepository.getMetadataByIds(newIds, undefined, false, true) + : await metadataRepository.getMetadataByIds(newIds); const elements = syncMetadata[collectionName] || []; this.registry.addList(builder, newIds); diff --git a/src/domain/metadata/builders/__tests__/MetadataPayloadBuilder.spec.tsx b/src/domain/metadata/builders/__tests__/MetadataPayloadBuilder.spec.tsx index 34b5c34ad..c88acb2f4 100644 --- a/src/domain/metadata/builders/__tests__/MetadataPayloadBuilder.spec.tsx +++ b/src/domain/metadata/builders/__tests__/MetadataPayloadBuilder.spec.tsx @@ -349,8 +349,10 @@ describe("MetadataPayloadBuilder", () => { if (includeObjectsAndReferences) { const metadataByIdsResponses = getDataSetMetadataByIdsResponsesWithIncludeAll(); + when( + mockedMetadataRepository.getMetadataByIds(anything(), anything(), anything(), anything()) + ).thenResolve(metadataByIdsResponses.first); when(mockedMetadataRepository.getMetadataByIds(anything())) - .thenResolve(metadataByIdsResponses.first) .thenResolve(metadataByIdsResponses.second) .thenResolve(metadataByIdsResponses.third) .thenResolve(metadataByIdsResponses.fourth) @@ -359,8 +361,12 @@ describe("MetadataPayloadBuilder", () => { .thenResolve(metadataByIdsResponses.seventh) .thenResolve(metadataByIdsResponses.eighth); } else { + when( + mockedMetadataRepository.getMetadataByIds(anything(), anything(), anything(), anything()) + ).thenResolve({ + dataSets: [getDataSetMetadata()], + }); when(mockedMetadataRepository.getMetadataByIds(anything())) - .thenResolve({ dataSets: [getDataSetMetadata()] }) .thenResolve({ dataSets: [getDataSetMetadata()], dataElements: [getDataElementDataSetMetadata()], diff --git a/src/domain/metadata/repositories/MetadataRepository.ts b/src/domain/metadata/repositories/MetadataRepository.ts index 179fae675..9aa6de2f7 100644 --- a/src/domain/metadata/repositories/MetadataRepository.ts +++ b/src/domain/metadata/repositories/MetadataRepository.ts @@ -13,7 +13,12 @@ import { import { MetadataImportParams } from "../entities/MetadataSynchronizationParams"; export interface MetadataRepository { - getMetadataByIds(ids: Id[], fields?: object | string, includeDefaults?: boolean): Promise>; + getMetadataByIds( + ids: Id[], + fields?: object | string, + includeDefaults?: boolean, + preserveNestedDefaultRefs?: boolean + ): Promise>; getByFilterRules(filterRules: FilterRule[]): Promise; getDefaultIds(filter?: string): Promise; getCategoryOptionCombos(): Promise<