Improve automatic placeholder creation (#6581)

* Describe placeholder resources in reponse

* Include details about placeholders in responses

* Add tests

* version bump

* Address review comments
This commit is contained in:
James Agnew 2025-01-02 19:38:13 -05:00 committed by GitHub
parent 5c4aae0b26
commit b75c34bba7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 222 additions and 24 deletions

View File

@ -49,7 +49,9 @@ public enum StorageResponseCodeEnum implements ICodingEnum {
SUCCESSFUL_PATCH_NO_CHANGE("Patch succeeded: No changes were detected so no action was taken."),
SUCCESSFUL_CONDITIONAL_PATCH("Conditional patch succeeded."),
SUCCESSFUL_CONDITIONAL_PATCH_NO_CHANGE(
"Conditional patch succeeded: No changes were detected so no action was taken.");
"Conditional patch succeeded: No changes were detected so no action was taken."),
AUTOMATICALLY_CREATED_PLACEHOLDER_RESOURCE("Automatically created placeholder resource."),
;
public static final String SYSTEM = "https://hapifhir.io/fhir/CodeSystem/hapi-fhir-storage-response-code";

View File

@ -116,6 +116,7 @@ ca.uhn.fhir.jpa.dao.BaseStorageDao.invalidVersion=Version "{0}" is not valid for
ca.uhn.fhir.jpa.dao.BaseStorageDao.multipleParamsWithSameNameOneIsMissingTrue=This server does not know how to handle multiple "{0}" parameters where one has a value of :missing=true
ca.uhn.fhir.jpa.dao.BaseStorageDao.missingBody=No body was supplied in request
ca.uhn.fhir.jpa.dao.BaseStorageDao.unableToDeleteNotFound=Unable to find resource matching URL "{0}". Nothing has been deleted.
ca.uhn.fhir.jpa.dao.BaseStorageDao.successfulAutoCreatePlaceholder=Automatically created placeholder resource with ID: {0}
ca.uhn.fhir.jpa.dao.BaseStorageDao.successfulCreate=Successfully created resource "{0}".
ca.uhn.fhir.jpa.dao.BaseStorageDao.successfulCreateConditionalNoMatch=Successfully conditionally created resource "{0}". No existing resources matched URL "{1}".
ca.uhn.fhir.jpa.dao.BaseStorageDao.successfulCreateConditionalWithMatch=Successfully conditionally created resource "{0}". Existing resource matched URL "{1}".

View File

@ -1490,7 +1490,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
}
}
populateOperationOutcomeForUpdate(w, outcome, theMatchUrl, outcome.getOperationType());
populateOperationOutcomeForUpdate(w, outcome, theMatchUrl, outcome.getOperationType(), theTransactionDetails);
return outcome;
}

View File

@ -621,7 +621,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
outcome.setId(theResource.getIdElement());
}
populateOperationOutcomeForUpdate(w, outcome, theMatchUrl, theOperationType);
populateOperationOutcomeForUpdate(w, outcome, theMatchUrl, theOperationType, theTransactionDetails);
return outcome;
}
@ -2465,6 +2465,9 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
}
if (!create) {
if (theTransactionDetails != null && theTransactionDetails.hasNullResolvedResourceId(resourceId)) {
create = true;
} else {
try {
entity = readEntityLatestVersion(
theRequest, resourceId, theRequestPartitionId, theTransactionDetails);
@ -2472,6 +2475,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
create = true;
}
}
}
if (create) {
outcome = doCreateForPostOrPut(
@ -2639,7 +2643,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
theRequest, savedEntity, theResource, null, RestOperationTypeEnum.UPDATE)
.setCreated(wasDeleted);
populateOperationOutcomeForUpdate(w, outcome, null, RestOperationTypeEnum.UPDATE);
populateOperationOutcomeForUpdate(w, outcome, null, RestOperationTypeEnum.UPDATE, theTransactionDetails);
return outcome;
}

View File

@ -311,7 +311,7 @@ public class TransactionProcessor extends BaseTransactionProcessor {
Map<IIdType, IResourceLookup<JpaPid>> outcomes = myIdHelperService.resolveResourceIdentities(
theRequestPartitionId, idsToPreResolve.keySet(), resolveMode);
for (Map.Entry<IIdType, IResourceLookup<JpaPid>> entry : outcomes.entrySet()) {
JpaPid next = (JpaPid) entry.getValue().getPersistentId();
JpaPid next = entry.getValue().getPersistentId();
IIdType unqualifiedVersionlessId = entry.getKey();
foundIds.add(unqualifiedVersionlessId.getValue());
theTransactionDetails.addResolvedResourceId(unqualifiedVersionlessId, next);

View File

@ -1,5 +1,6 @@
package ca.uhn.fhir.jpa.dao.r4;
import static ca.uhn.fhir.util.HapiExtensions.EXT_RESOURCE_PLACEHOLDER;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@ -24,15 +25,19 @@ import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.AuditEvent;
import org.hl7.fhir.r4.model.BooleanType;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Enumerations;
import org.hl7.fhir.r4.model.Extension;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Identifier;
import org.hl7.fhir.r4.model.Observation;
import org.hl7.fhir.r4.model.Observation.ObservationStatus;
import org.hl7.fhir.r4.model.OperationOutcome;
import org.hl7.fhir.r4.model.Organization;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.r4.model.Resource;
import org.hl7.fhir.r4.model.ResourceType;
import org.hl7.fhir.r4.model.SearchParameter;
import org.hl7.fhir.r4.model.Task;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
@ -96,14 +101,25 @@ public class FhirResourceDaoCreatePlaceholdersR4Test extends BaseJpaR4Test {
task.addPartOf().setReference("Task/AAA");
task.addPartOf().setReference("Task/AAA");
task.addPartOf().setReference("Task/AAA");
IIdType id = myTaskDao.create(task).getId().toUnqualifiedVersionless();
DaoMethodOutcome methodOutcome = myTaskDao.create(task);
OperationOutcome oo = (OperationOutcome) methodOutcome.getOperationOutcome();
ourLog.info(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo));
assertEquals("SUCCESSFUL_CREATE", oo.getIssue().get(0).getDetails().getCodingFirstRep().getCode());
assertEquals("AUTOMATICALLY_CREATED_PLACEHOLDER_RESOURCE", oo.getIssue().get(1).getDetails().getCodingFirstRep().getCode());
assertEquals("Automatically created placeholder resource with ID: Task/AAA/_history/1", oo.getIssue().get(1).getDiagnostics());
IIdType id = methodOutcome.getId().toUnqualifiedVersionless();
task = myTaskDao.read(id);
assertThat(task.getPartOf()).hasSize(3);
assertEquals("Task/AAA", task.getPartOf().get(0).getReference());
assertEquals("Task/AAA", task.getPartOf().get(1).getReference());
assertEquals("Task/AAA", task.getPartOf().get(2).getReference());
Task placeholderTask = myTaskDao.read(new IdType("Task/AAA"), mySrd);
ourLog.info(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(placeholderTask));
assertNotNull(placeholderTask);
SearchParameterMap params = new SearchParameterMap();
params.add(Task.SP_PART_OF, new ReferenceParam("Task/AAA"));
List<String> found = toUnqualifiedVersionlessIdValues(myTaskDao.search(params));
@ -222,7 +238,7 @@ public class FhirResourceDaoCreatePlaceholdersR4Test extends BaseJpaR4Test {
IIdType placeholderPatId = placeholderPat.getIdElement();
ourLog.debug("\nPlaceholder Patient created:\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(placeholderPat));
assertThat(placeholderPat.getIdentifier()).isEmpty();
Extension extension = placeholderPat.getExtensionByUrl(HapiExtensions.EXT_RESOURCE_PLACEHOLDER);
Extension extension = placeholderPat.getExtensionByUrl(EXT_RESOURCE_PLACEHOLDER);
assertNotNull(extension);
assertTrue(extension.hasValue());
assertTrue(((BooleanType) extension.getValue()).booleanValue());
@ -240,7 +256,7 @@ public class FhirResourceDaoCreatePlaceholdersR4Test extends BaseJpaR4Test {
Patient updatedPat = myPatientDao.read(updatedPatId);
ourLog.debug("\nUpdated Patient:\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(updatedPat));
assertThat(updatedPat.getIdentifier()).hasSize(1);
extension = updatedPat.getExtensionByUrl(HapiExtensions.EXT_RESOURCE_PLACEHOLDER);
extension = updatedPat.getExtensionByUrl(EXT_RESOURCE_PLACEHOLDER);
assertNull(extension);
}
@ -653,7 +669,40 @@ public class FhirResourceDaoCreatePlaceholdersR4Test extends BaseJpaR4Test {
}
@Test
public void testAutocreatePlaceholderTest() {
public void testSearchForPlaceholder() {
//Setup
myStorageSettings.setAutoCreatePlaceholderReferenceTargets(true);
SearchParameter sp = new SearchParameter();
sp.addBase("Patient");
sp.setStatus(Enumerations.PublicationStatus.ACTIVE);
sp.setCode("placeholder");
sp.setName("placeholder");
sp.setType(Enumerations.SearchParamType.TOKEN);
sp.setExpression("extension('" + EXT_RESOURCE_PLACEHOLDER + "').where(value = true)");
sp.setXpathUsage(SearchParameter.XPathUsageType.NORMAL);
sp.setDescription("Index resources which were automatically created as placeholders");
ourLog.info("Search parameter:\n{}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(sp));
mySearchParameterDao.create(sp, mySrd);
createPatient(withId("B"), withActiveTrue());
// Test
Observation obsToCreate = new Observation();
obsToCreate.getSubject().setReference("Patient/A");
myObservationDao.create(obsToCreate, mySrd);
logAllTokenIndexes("placeholder");
// Verify
SearchParameterMap map = SearchParameterMap.newSynchronous("placeholder", new TokenParam("true"));
IBundleProvider outcome = myPatientDao.search(map, mySrd);
assertThat(toUnqualifiedVersionlessIdValues(outcome)).asList().containsExactly("Patient/A");
}
@Test
public void testTransaction() {
myStorageSettings.setAutoCreatePlaceholderReferenceTargets(true);
Observation obs = new Observation();
@ -663,7 +712,12 @@ public class FhirResourceDaoCreatePlaceholdersR4Test extends BaseJpaR4Test {
BundleBuilder builder = new BundleBuilder(myFhirContext);
builder.addTransactionUpdateEntry(obs);
mySystemDao.transaction(new SystemRequestDetails(), (Bundle) builder.getBundle());
Bundle outcome = mySystemDao.transaction(new SystemRequestDetails(), (Bundle) builder.getBundle());
ourLog.info(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome));
OperationOutcome oo = (OperationOutcome) outcome.getEntry().get(0).getResponse().getOutcome();
assertEquals("SUCCESSFUL_UPDATE_AS_CREATE", oo.getIssue().get(0).getDetails().getCodingFirstRep().getCode());
assertEquals("AUTOMATICALLY_CREATED_PLACEHOLDER_RESOURCE", oo.getIssue().get(1).getDetails().getCodingFirstRep().getCode());
assertEquals("Automatically created placeholder resource with ID: Patient/RED/_history/1", oo.getIssue().get(1).getDiagnostics());
// verify subresource is created
Patient returned = myPatientDao.read(patientRef.getReferenceElement());
@ -672,7 +726,7 @@ public class FhirResourceDaoCreatePlaceholdersR4Test extends BaseJpaR4Test {
@Test
public void testAutocreatePlaceholderWithTargetExistingAlreadyTest() {
public void testTransaction_TargetExistingAlready() {
myStorageSettings.setAutoCreatePlaceholderReferenceTargets(true);
myStorageSettings.setAutoVersionReferenceAtPaths("Observation.subject");
@ -713,7 +767,7 @@ public class FhirResourceDaoCreatePlaceholdersR4Test extends BaseJpaR4Test {
* This test is the same as above, except it uses the serverid (instead of forcedid)
*/
@Test
public void testAutocreatePlaceholderWithExistingTargetWithServerAssignedIdTest() {
public void testTransaction_ExistingTargetWithServerAssignedId() {
myStorageSettings.setAutoCreatePlaceholderReferenceTargets(true);
myStorageSettings.setAutoVersionReferenceAtPaths("Observation.subject");

View File

@ -116,6 +116,7 @@ import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiFunction;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@ -2741,6 +2742,51 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test
}
@Test
public void testTransactionWithCreatePlaceholders() {
// Setup
myStorageSettings.setAutoCreatePlaceholderReferenceTargets(true);
BiFunction<String, String, Patient> supplier = (patientId, orgRef) -> {
Patient patient = new Patient();
patient.setId(patientId);
patient.setManagingOrganization(new Reference(orgRef));
return patient;
};
BundleBuilder bb = new BundleBuilder(myFhirContext);
bb.addTransactionUpdateEntry(supplier.apply("Patient/P0", "Organization/O0"));
bb.addTransactionUpdateEntry(supplier.apply("Patient/P1", "Organization/O0"));
bb.addTransactionUpdateEntry(supplier.apply("Patient/P2", "Organization/O1"));
bb.addTransactionUpdateEntry(supplier.apply("Patient/P3", "Organization/O1"));
bb.addTransactionUpdateEntry(supplier.apply("Patient/P4", "Organization/O2"));
bb.addTransactionUpdateEntry(supplier.apply("Patient/P5", "Organization/O2"));
Bundle input = bb.getBundleTyped();
// Test
myCaptureQueriesListener.clear();
Bundle output = mySystemDao.transaction(mySrd, input);
ourLog.info(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(output));
// Verify
assertEquals(1, myCaptureQueriesListener.countSelectQueries());
assertEquals(30, myCaptureQueriesListener.countInsertQueries());
assertEquals(0, myCaptureQueriesListener.countUpdateQueries());
assertEquals(0, myCaptureQueriesListener.countDeleteQueries());
assertEquals(0, myCaptureQueriesListener.countInsertQueriesRepeated());
assertEquals(1, myCaptureQueriesListener.countGetConnections());
myCaptureQueriesListener.logSelectQueries();
for (int i = 0; i < 5; i++) {
assertNotGone(new IdType("Patient/P" + i));
}
for (int i = 0; i < 3; i++) {
assertNotGone(new IdType("Organization/O" + i));
}
}
/**
* See the class javadoc before changing the counts in this test!

View File

@ -71,6 +71,7 @@ public class TransactionDetails {
private ListMultimap<Pointcut, HookParams> myDeferredInterceptorBroadcasts;
private EnumSet<Pointcut> myDeferredInterceptorBroadcastPointcuts;
private boolean myFhirTransaction;
private List<IIdType> myAutoCreatedPlaceholderResources = Collections.emptyList();
/**
* Constructor
@ -228,6 +229,24 @@ public class TransactionDetails {
return false;
}
/**
* Returns true if the given ID was marked as not existing (i.e. someone called
* {@link #addResolvedResourceId(IIdType, IResourcePersistentId)} with an
* ID of null).
*
* @param theId The resource ID
* @since 8.0.0
*/
public boolean hasNullResolvedResourceId(IIdType theId) {
if (myResolvedResourceIds != null) {
String key = theId.toVersionless().getValue();
if (myResolvedResourceIds.containsKey(key)) {
return myResolvedResourceIds.get(key) == null;
}
}
return false;
}
/**
* A <b>Resolved Resource ID</b> is a mapping between a resource ID (e.g. "<code>Patient/ABC</code>" or
* "<code>Observation/123</code>") and a storage ID for that resource. Resources should only be placed within
@ -292,8 +311,8 @@ public class TransactionDetails {
}
/**
* @since 6.8.0
* @see #addResolvedMatchUrl(FhirContext, String, IResourcePersistentId)
* @since 6.8.0
*/
public void removeResolvedMatchUrl(String theMatchUrl) {
myResolvedMatchUrls.remove(theMatchUrl);
@ -442,4 +461,20 @@ public class TransactionDetails {
public void setFhirTransaction(boolean theFhirTransaction) {
myFhirTransaction = theFhirTransaction;
}
public void addAutoCreatedPlaceholderResource(IIdType theResource) {
if (myAutoCreatedPlaceholderResources.isEmpty()) {
myAutoCreatedPlaceholderResources = new ArrayList<>();
}
myAutoCreatedPlaceholderResources.add(theResource);
}
@Nonnull
public List<IIdType> getAutoCreatedPlaceholderResourcesAndClear() {
List<IIdType> retVal = myAutoCreatedPlaceholderResources;
if (!myAutoCreatedPlaceholderResources.isEmpty()) {
myAutoCreatedPlaceholderResources = Collections.emptyList();
}
return retVal;
}
}

View File

@ -104,6 +104,7 @@ public abstract class BaseStorageDao {
protected static final String MESSAGE_KEY_DELETE_RESOURCE_NOT_EXISTING = "deleteResourceNotExisting";
protected static final String MESSAGE_KEY_DELETE_RESOURCE_ALREADY_DELETED = "deleteResourceAlreadyDeleted";
public static final String OO_ISSUE_CODE_INFORMATIONAL = "informational";
@Autowired
protected ISearchParamRegistry mySearchParamRegistry;
@ -452,7 +453,8 @@ public abstract class BaseStorageDao {
public IBaseOperationOutcome createInfoOperationOutcome(
String theMessage, @Nullable StorageResponseCodeEnum theStorageResponseCode) {
return createOperationOutcome(OO_SEVERITY_INFO, theMessage, "informational", theStorageResponseCode);
return createOperationOutcome(
OO_SEVERITY_INFO, theMessage, OO_ISSUE_CODE_INFORMATIONAL, theStorageResponseCode);
}
private IBaseOperationOutcome createOperationOutcome(String theSeverity, String theMessage, String theCode) {
@ -592,7 +594,8 @@ public abstract class BaseStorageDao {
@Nullable StopWatch theItemStopwatch,
DaoMethodOutcome theMethodOutcome,
String theMatchUrl,
RestOperationTypeEnum theOperationType) {
RestOperationTypeEnum theOperationType,
TransactionDetails theTransactionDetails) {
String msg;
StorageResponseCodeEnum outcome;
@ -723,10 +726,42 @@ public abstract class BaseStorageDao {
msg = msg + " " + msgSuffix;
}
theMethodOutcome.setOperationOutcome(createInfoOperationOutcome(msg, outcome));
IBaseOperationOutcome oo = createInfoOperationOutcome(msg, outcome);
if (theTransactionDetails != null) {
List<IIdType> autoCreatedPlaceholderResources =
theTransactionDetails.getAutoCreatedPlaceholderResourcesAndClear();
for (IIdType next : autoCreatedPlaceholderResources) {
msg = addIssueToOperationOutcomeForAutoCreatedPlaceholder(getContext(), next, oo);
}
}
theMethodOutcome.setOperationOutcome(oo);
ourLog.debug(msg);
}
public static String addIssueToOperationOutcomeForAutoCreatedPlaceholder(
FhirContext theFhirContext, IIdType thePlaceholderId, IBaseOperationOutcome theOperationOutcomeToPopulate) {
String msg;
msg = theFhirContext
.getLocalizer()
.getMessageSanitized(BaseStorageDao.class, "successfulAutoCreatePlaceholder", thePlaceholderId);
String detailSystem = StorageResponseCodeEnum.AUTOMATICALLY_CREATED_PLACEHOLDER_RESOURCE.getSystem();
String detailCode = StorageResponseCodeEnum.AUTOMATICALLY_CREATED_PLACEHOLDER_RESOURCE.getCode();
String detailDescription = StorageResponseCodeEnum.AUTOMATICALLY_CREATED_PLACEHOLDER_RESOURCE.getDisplay();
OperationOutcomeUtil.addIssue(
theFhirContext,
theOperationOutcomeToPopulate,
OO_SEVERITY_INFO,
msg,
null,
OO_ISSUE_CODE_INFORMATIONAL,
detailSystem,
detailCode,
detailDescription);
return msg;
}
/**
* Extracts a list of references that have versions in their ID whose versions should not be stripped
*

View File

@ -98,6 +98,7 @@ import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseBinary;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IBaseReference;
import org.hl7.fhir.instance.model.api.IBaseResource;
@ -1946,9 +1947,19 @@ public abstract class BaseTransactionProcessor {
theIdSubstitutions.updateTargets(newId);
if (theDaoMethodOutcome.getOperationOutcome() != null) {
// This will only be null if we're not intending to return an OO
IBaseOperationOutcome operationOutcome = theDaoMethodOutcome.getOperationOutcome();
if (operationOutcome != null) {
List<IIdType> autoCreatedPlaceholders =
theTransactionDetails.getAutoCreatedPlaceholderResourcesAndClear();
for (IIdType autoCreatedPlaceholder : autoCreatedPlaceholders) {
BaseStorageDao.addIssueToOperationOutcomeForAutoCreatedPlaceholder(
myContext, autoCreatedPlaceholder, operationOutcome);
}
IBase responseEntry = theEntriesToProcess.getResponseBundleEntryWithVersionlessComparison(newId);
myVersionAdapter.setResponseOutcome(responseEntry, theDaoMethodOutcome.getOperationOutcome());
myVersionAdapter.setResponseOutcome(responseEntry, operationOutcome);
}
}
}

View File

@ -126,6 +126,14 @@ public class DaoResourceLinkResolver<T extends IResourcePersistentId<?>> impleme
String idPart = targetResourceId.getIdPart();
try {
if (persistentId == null) {
// If we previously looked up the ID, and it was not found, don't bother
// looking it up again
if (theTransactionDetails != null
&& theTransactionDetails.hasNullResolvedResourceId(targetResourceId)) {
throw new ResourceNotFoundException(Msg.code(2602));
}
resolvedResource = myIdHelperService.resolveResourceIdentity(
theRequestPartitionId,
resourceType,
@ -294,7 +302,9 @@ public class DaoResourceLinkResolver<T extends IResourcePersistentId<?>> impleme
theTransactionDetails.addRollbackUndoAction(() -> newResource.setId(existingId));
}
newResource.setId(resName + "/" + theIdToAssignToPlaceholder);
valueOf = placeholderResourceDao.update(newResource, theRequest).getEntity();
valueOf = placeholderResourceDao
.update(newResource, null, true, false, theRequest, theTransactionDetails)
.getEntity();
} else {
valueOf = placeholderResourceDao.create(newResource, theRequest).getEntity();
}
@ -303,6 +313,7 @@ public class DaoResourceLinkResolver<T extends IResourcePersistentId<?>> impleme
persistentId = myIdHelperService.newPid(persistentId.getId());
persistentId.setAssociatedResourceId(valueOf.getIdDt());
theTransactionDetails.addResolvedResourceId(persistentId.getAssociatedResourceId(), persistentId);
theTransactionDetails.addAutoCreatedPlaceholderResource(newResource.getIdElement());
}
return Optional.ofNullable(valueOf);

View File

@ -3304,4 +3304,3 @@
</profile>
</profiles>
</project>