From e44b04ea6db9482c7b298ed7a984524a79b7197c Mon Sep 17 00:00:00 2001 From: Alan Lail Date: Thu, 20 Aug 2026 10:57:53 -0400 Subject: [PATCH] =?UTF-8?q?toJSON()=20now=20sets=20result.caseVersion=20?= =?UTF-8?q?=3D=20effectiveVersion=20when=20serving=20as=20CASE=201.1,=20an?= =?UTF-8?q?d=20strips=20the=201.1-only=20fields=20(frameworkType,=20subjec?= =?UTF-8?q?tURI,=20extensions)=20otherwise=20=E2=80=94=20replacing=20the?= =?UTF-8?q?=20old=20logic=20that=20only=20ever=20stripped=20fields=20and?= =?UTF-8?q?=20never=20added=20caseVersion=20back.=20This=20fixes=20getCFDo?= =?UTF-8?q?cument=20and=20getCFPackage=20(both=20call=20document.toJSON())?= =?UTF-8?q?=20without=20touching=20GetAllCFDocuments,=20which=20was=20alre?= =?UTF-8?q?ady=20correct=20via=20its=20own=20hand-built=20path.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../endpoints/__tests__/GetCFDocument.test.ts | 62 +++++++++++++ .../endpoints/__tests__/GetCFPackage.test.ts | 62 +++++++++++++ .../src/domain/case/entities/CFDocument.ts | 8 +- .../entities/__tests__/CFDocument.test.ts | 93 ++++++++++++++++++- 4 files changed, 221 insertions(+), 4 deletions(-) diff --git a/apps/opencase/src/application/case/endpoints/__tests__/GetCFDocument.test.ts b/apps/opencase/src/application/case/endpoints/__tests__/GetCFDocument.test.ts index 6fbf939..ae16c56 100644 --- a/apps/opencase/src/application/case/endpoints/__tests__/GetCFDocument.test.ts +++ b/apps/opencase/src/application/case/endpoints/__tests__/GetCFDocument.test.ts @@ -60,6 +60,68 @@ describe('GetCFDocument', () => { }) })) }) + + it('should include caseVersion for a CASE 1.1 document', async () => { + const document = CFDocument.create({ + tenantId, + caseVersion: '1.1', + sourcedId: docId, + uri: `/ims/case/v1p1/CFDocuments/${docId}`, + creator: 'Test Creator', + title: 'Test Document', + lastChangeDateTime: new Date('2024-01-01T00:00:00Z') + }) + + const pkg = new CFPackage({ document, items: [], associations: [], rubrics: [] }) + mockRepository.load.mockResolvedValue(pkg) + + const result = await getCFDocument.execute({ tenantId, caseVersion: '1.1', sourcedId: docId }) + + expect(result.caseVersion).toBe('1.1') + }) + + it('should not include caseVersion for a CASE 1.0 document', async () => { + const document = CFDocument.create({ + tenantId, + caseVersion: '1.0', + sourcedId: docId, + uri: `/ims/case/v1p0/CFDocuments/${docId}`, + creator: 'Test Creator', + title: 'Test Document', + lastChangeDateTime: new Date('2024-01-01T00:00:00Z') + }) + + const pkg = new CFPackage({ document, items: [], associations: [], rubrics: [] }) + mockRepository.load.mockResolvedValue(pkg) + + const result = await getCFDocument.execute({ tenantId, caseVersion: '1.0', sourcedId: docId }) + + expect(result.caseVersion).toBeUndefined() + }) + + it('should not include caseVersion when downconverting a stored 1.1 document to 1.0', async () => { + const document = CFDocument.create({ + tenantId, + caseVersion: '1.1', + sourcedId: docId, + uri: `/ims/case/v1p1/CFDocuments/${docId}`, + creator: 'Test Creator', + title: 'Test Document', + lastChangeDateTime: new Date('2024-01-01T00:00:00Z') + }) + + const pkg = new CFPackage({ document, items: [], associations: [], rubrics: [] }) + mockRepository.load.mockResolvedValue(pkg) + + const result = await getCFDocument.execute({ + tenantId, + caseVersion: '1.0', + loadVersion: '1.1', + sourcedId: docId + }) + + expect(result.caseVersion).toBeUndefined() + }) }) }) diff --git a/apps/opencase/src/application/case/endpoints/__tests__/GetCFPackage.test.ts b/apps/opencase/src/application/case/endpoints/__tests__/GetCFPackage.test.ts index 26fd0e4..2484583 100644 --- a/apps/opencase/src/application/case/endpoints/__tests__/GetCFPackage.test.ts +++ b/apps/opencase/src/application/case/endpoints/__tests__/GetCFPackage.test.ts @@ -237,6 +237,68 @@ describe('GetCFPackage', () => { expect(result).not.toBeNull(); expect(result?.CFDocument.adoptionStatus).toBe('Deprecated'); }); + + it('should include caseVersion on the embedded CFDocument for a CASE 1.1 package', async () => { + const document = CFDocument.create({ + tenantId, + caseVersion: '1.1', + sourcedId: docId, + uri: `/ims/case/v1p1/CFDocuments/${docId}`, + creator: 'Test Creator', + title: 'Test Document', + lastChangeDateTime: new Date('2024-01-01T00:00:00Z') + }); + + const pkg = new CFPackage({ document, items: [], associations: [], rubrics: [] }); + mockRepository.load.mockResolvedValue(pkg); + + const result = await getCFPackage.execute({ tenantId, caseVersion: '1.1', docId }); + + expect(result?.CFDocument.caseVersion).toBe('1.1'); + }); + + it('should not include caseVersion on the embedded CFDocument for a CASE 1.0 package', async () => { + const document = CFDocument.create({ + tenantId, + caseVersion: '1.0', + sourcedId: docId, + uri: `/ims/case/v1p0/CFDocuments/${docId}`, + creator: 'Test Creator', + title: 'Test Document', + lastChangeDateTime: new Date('2024-01-01T00:00:00Z') + }); + + const pkg = new CFPackage({ document, items: [], associations: [], rubrics: [] }); + mockRepository.load.mockResolvedValue(pkg); + + const result = await getCFPackage.execute({ tenantId, caseVersion: '1.0', docId }); + + expect(result?.CFDocument.caseVersion).toBeUndefined(); + }); + + it('should not include caseVersion when downconverting a stored 1.1 package to 1.0', async () => { + const document = CFDocument.create({ + tenantId, + caseVersion: '1.1', + sourcedId: docId, + uri: `/ims/case/v1p1/CFDocuments/${docId}`, + creator: 'Test Creator', + title: 'Test Document', + lastChangeDateTime: new Date('2024-01-01T00:00:00Z') + }); + + const pkg = new CFPackage({ document, items: [], associations: [], rubrics: [] }); + mockRepository.load.mockResolvedValue(pkg); + + const result = await getCFPackage.execute({ + tenantId, + caseVersion: '1.0', + loadVersion: '1.1', + docId + }); + + expect(result?.CFDocument.caseVersion).toBeUndefined(); + }); }); }); diff --git a/apps/opencase/src/domain/case/entities/CFDocument.ts b/apps/opencase/src/domain/case/entities/CFDocument.ts index be158f5..b57fe8d 100644 --- a/apps/opencase/src/domain/case/entities/CFDocument.ts +++ b/apps/opencase/src/domain/case/entities/CFDocument.ts @@ -170,13 +170,15 @@ export class CFDocument { }; } - // CASE 1.0 strictness: do not emit CASE 1.1-only fields - if (effectiveVersion === '1.0') { + // CASE 1.1: include caseVersion (spec best practice). CASE 1.0: strip 1.1-only fields. + if (effectiveVersion === '1.1') { + result.caseVersion = effectiveVersion + } else { delete result.frameworkType delete result.subjectURI delete result.extensions } - + return result; } } diff --git a/apps/opencase/src/domain/case/entities/__tests__/CFDocument.test.ts b/apps/opencase/src/domain/case/entities/__tests__/CFDocument.test.ts index 5164c66..241061f 100644 --- a/apps/opencase/src/domain/case/entities/__tests__/CFDocument.test.ts +++ b/apps/opencase/src/domain/case/entities/__tests__/CFDocument.test.ts @@ -130,7 +130,6 @@ describe('CFDocument', () => { expect(json.title).toBe('Test Document'); expect(json.lastChangeDateTime).toBe('2024-01-01T12:30:45.000Z'); expect(json.tenantId).toBeUndefined(); - expect(json.caseVersion).toBeUndefined(); expect(json.sourcedId).toBeUndefined(); }); @@ -166,6 +165,98 @@ describe('CFDocument', () => { expect(json.notes).toBe('Test notes'); expect(json.extensions).toEqual({ custom: { key: 'value' } }); }); + + it('should include caseVersion when the document is CASE 1.1', () => { + const props = { + tenantId, + caseVersion: '1.1' as CaseVersion, + sourcedId: 'doc-123', + uri: '/ims/case/v1p1/CFDocuments/doc-123', + creator: 'Test Creator', + title: 'Test Document', + lastChangeDateTime: new Date('2024-01-01T00:00:00Z') + }; + + const doc = CFDocument.create(props); + const json = doc.toJSON(); + + expect(json.caseVersion).toBe('1.1'); + }); + + it('should not include caseVersion when the document is CASE 1.0', () => { + const props = { + tenantId, + caseVersion: '1.0' as CaseVersion, + sourcedId: 'doc-123', + uri: '/ims/case/v1p0/CFDocuments/doc-123', + creator: 'Test Creator', + title: 'Test Document', + lastChangeDateTime: new Date('2024-01-01T00:00:00Z') + }; + + const doc = CFDocument.create(props); + const json = doc.toJSON(); + + expect(json.caseVersion).toBeUndefined(); + }); + + it('should honor serializeAs override when downconverting a 1.1 document to 1.0', () => { + const props = { + tenantId, + caseVersion: '1.1' as CaseVersion, + sourcedId: 'doc-123', + uri: '/ims/case/v1p1/CFDocuments/doc-123', + creator: 'Test Creator', + title: 'Test Document', + frameworkType: 'Competency', + lastChangeDateTime: new Date('2024-01-01T00:00:00Z') + }; + + const doc = CFDocument.create(props); + const json = doc.toJSON('1.0'); + + expect(json.caseVersion).toBeUndefined(); + expect(json.frameworkType).toBeUndefined(); + }); + + it('should honor serializeAs override when serving a 1.0 document via v1p1', () => { + const props = { + tenantId, + caseVersion: '1.0' as CaseVersion, + sourcedId: 'doc-123', + uri: '/ims/case/v1p0/CFDocuments/doc-123', + creator: 'Test Creator', + title: 'Test Document', + lastChangeDateTime: new Date('2024-01-01T00:00:00Z') + }; + + const doc = CFDocument.create(props); + const json = doc.toJSON('1.1'); + + expect(json.caseVersion).toBe('1.1'); + }); + + it('should strip 1.1-only fields (frameworkType, subjectURI, extensions) for CASE 1.0', () => { + const props = { + tenantId, + caseVersion: '1.1' as CaseVersion, + sourcedId: 'doc-123', + uri: '/ims/case/v1p1/CFDocuments/doc-123', + creator: 'Test Creator', + title: 'Test Document', + frameworkType: 'Competency', + subjectURI: [{ identifier: 'subj-1', uri: '/ims/case/v1p1/CFItems/subj-1', title: 'Subject' }], + extensions: { custom: { key: 'value' } }, + lastChangeDateTime: new Date('2024-01-01T00:00:00Z') + }; + + const doc = CFDocument.create(props); + const json = doc.toJSON('1.0'); + + expect(json.frameworkType).toBeUndefined(); + expect(json.subjectURI).toBeUndefined(); + expect(json.extensions).toBeUndefined(); + }); }); });