diff --git a/plugins/process-resources/src/__tests__/detectSlotsRefined.test.ts b/plugins/process-resources/src/__tests__/detectSlotsRefined.test.ts index 1a3e62c98e..bd84a43a49 100644 --- a/plugins/process-resources/src/__tests__/detectSlotsRefined.test.ts +++ b/plugins/process-resources/src/__tests__/detectSlotsRefined.test.ts @@ -1138,4 +1138,84 @@ describe('detectSlots refined', () => { expect(subProcSlot).toBeDefined() expect(subProcSlot.memberOf).toBeDefined() // It should refer to slot created for ClassB }) + + test('Test 24: User Example (Execute-2.json simulation)', async () => { + const mainClassId = 'ClassA' + const testResultClassId = '69e8d19c618de58dbaf9ecf2' + const attemptsAttrId = 'custom6a00914a295035d76ab16e1d' + const attemptNumAttrId = 'custom69eb7f41889a0214e41368cb' + + const mainClassDoc = { _id: mainClassId, _class: 'core:class:Class', name: 'ClassA', label: 'Main Entity' } + const testResultClassDoc = { + _id: testResultClassId, + _class: 'core:class:Class', + name: 'TestResult', + label: '(T) Test Result' + } + + const attemptsAttrDoc = { + _id: attemptsAttrId, + _class: 'core:class:Attribute', + name: 'attempts', + label: 'Attempts', + attributeOf: mainClassId, + type: 'core:class:TypeNumber' + } + const attemptNumAttrDoc = { + _id: attemptNumAttrId, + _class: 'core:class:Attribute', + name: 'attemptNumber', + label: 'Attempt number', + attributeOf: testResultClassId, + type: 'core:class:TypeNumber' + } + + m.findObject = jest.fn().mockImplementation((id) => { + if (id === mainClassId) return mainClassDoc + if (id === testResultClassId) return testResultClassDoc + if (id === attemptsAttrId) return attemptsAttrDoc + if (id === attemptNumAttrId) return attemptNumAttrDoc + if (id === 'process:method:CreateCard') return { _id: 'process:method:CreateCard', requiredParams: ['_class'] } + return undefined + }) + + h.findAttribute = jest.fn().mockImplementation((tag, attrId) => { + if (tag === mainClassId && attrId === attemptsAttrId) return attemptsAttrDoc + if (tag === testResultClassId && attrId === attemptNumAttrId) return attemptNumAttrDoc + return undefined + }) + + const transitions = [ + { + _id: 'tr1', + actions: [ + { + _id: 'act1', + methodId: 'process:method:CreateCard', + params: { + _class: testResultClassId, + [attemptNumAttrId]: `\${@${attemptsAttrId}}` + } + } + ] + } + ] + + const slots: Record = {} + const bindings: Record = {} + detectSlots({ masterTag: mainClassId } as any, transitions as any[], slots, bindings, m, h) + + // attempts (attemptsAttrId) belongs to mainClassId (masterTag), so its memberOf must be undefined. + // attemptNumber (attemptNumAttrId) belongs to testResultClassId, so its memberOf must point to the testResultClassId slot. + const attemptsSlotName = 'attempts' + const attemptNumSlotName = 'attemptNumber' + const testResultSlotName = '(T) Test Result' + + expect(slots[attemptsSlotName]).toBeDefined() + expect(slots[attemptNumSlotName]).toBeDefined() + expect(slots[testResultSlotName]).toBeDefined() + + expect(slots[attemptsSlotName].memberOf).toBeUndefined() + expect(slots[attemptNumSlotName].memberOf).toBe(`__SLOT_${testResultSlotName}__`) + }) }) diff --git a/plugins/process-resources/src/exporter.ts b/plugins/process-resources/src/exporter.ts index c91fbbfacd..e7c904ce70 100644 --- a/plugins/process-resources/src/exporter.ts +++ b/plugins/process-resources/src/exporter.ts @@ -552,15 +552,18 @@ function resolveActionContext ( * its `__SLOT_name__` reference for use as `memberOf`. */ function resolveParentSlot ( - targetTag: Ref, + targetTag: Ref | undefined, masterTag: Ref, memberOfRef: string | undefined, getOrAddSlot: (id: string, model: DetailedSlotModel) => string, m: ModelDb ): string | undefined { - if (targetTag === undefined || targetTag === '' || normalizeId(targetTag) === normalizeId(masterTag)) { + if (targetTag === undefined || targetTag === '') { return memberOfRef } + if (normalizeId(targetTag) === normalizeId(masterTag)) { + return undefined + } const targetDoc = m.findObject(targetTag) as Class | undefined const parentSlotId = getOrAddSlot( targetTag, @@ -807,7 +810,8 @@ function scanParamKey ( if (isSystemId(parentClassId) && !isContextualValue(val, activeTag, masterTag, h, m)) { return } - getOrAddSlot(key, attributeToSlot(keyAttr, memberOfRef)) + const attrMemberOf = resolveParentSlot(keyAttr.attributeOf, masterTag, memberOfRef, getOrAddSlot, m) + getOrAddSlot(key, attributeToSlot(keyAttr, attrMemberOf)) } else if (key.length >= 24) { getOrAddSlot(key, unknownToSlot(key, memberOfRef)) } @@ -872,7 +876,9 @@ function classifyAndRegisterSlot ( const assocMemberOf = resolveParentSlot(targetClass, masterTag, memberOfRef, getOrAddSlot, m) getOrAddSlot(id, associationToSlot(assoc, direction, assocMemberOf)) } else if (classId === normalizeId(core.class.Attribute)) { - getOrAddSlot(id, attributeToSlot(obj as Attribute, memberOfRef)) + const attr = obj as Attribute + const attrMemberOf = resolveParentSlot(attr.attributeOf, masterTag, memberOfRef, getOrAddSlot, m) + getOrAddSlot(id, attributeToSlot(attr, attrMemberOf)) } else if (classId === normalizeId(core.class.Class) || h.isDerived(obj._class, core.class.Class)) { getOrAddSlot(id, classToSlot(obj as any as Class, memberOfRef)) } else {