001/*- 002 * #%L 003 * HAPI FHIR Storage api 004 * %% 005 * Copyright (C) 2014 - 2024 Smile CDR, Inc. 006 * %% 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 * #L% 019 */ 020package ca.uhn.fhir.jpa.api.svc; 021 022import ca.uhn.fhir.context.FhirContext; 023import ca.uhn.fhir.i18n.Msg; 024import ca.uhn.fhir.interceptor.model.RequestPartitionId; 025import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; 026import ca.uhn.fhir.jpa.api.model.PersistentIdToForcedIdMap; 027import ca.uhn.fhir.jpa.model.cross.IResourceLookup; 028import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; 029import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; 030import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; 031import jakarta.annotation.Nonnull; 032import jakarta.annotation.Nullable; 033import org.hl7.fhir.instance.model.api.IAnyResource; 034import org.hl7.fhir.instance.model.api.IBaseResource; 035import org.hl7.fhir.instance.model.api.IIdType; 036 037import java.util.Collection; 038import java.util.Date; 039import java.util.List; 040import java.util.Map; 041import java.util.Optional; 042import java.util.Set; 043import java.util.stream.Collectors; 044 045/** 046 * This interface is used to translate between {@link IResourcePersistentId} 047 * and actual resource IDs. 048 */ 049public interface IIdHelperService<T extends IResourcePersistentId<?>> { 050 051 /** 052 * Given a persistent ID, returns the associated resource ID 053 */ 054 @Nonnull 055 IIdType translatePidIdToForcedId(FhirContext theCtx, String theResourceType, T theId); 056 057 /** 058 * @param theResourceType Note that it is inefficient to call this method 059 * with a null resource type, so this should be avoided 060 * unless strictly necessary. 061 * @throws ResourceNotFoundException If the ID can not be found 062 */ 063 @Nonnull 064 IResourceLookup<T> resolveResourceIdentity( 065 @Nonnull RequestPartitionId theRequestPartitionId, 066 @Nullable String theResourceType, 067 @Nonnull String theResourceId, 068 @Nonnull ResolveIdentityMode theMode) 069 throws ResourceNotFoundException; 070 071 /** 072 * @param theResourceType Note that it is inefficient to call this method 073 * with a null resource type, so this should be avoided 074 * unless strictly necessary. 075 * @throws ResourceNotFoundException If the ID can not be found 076 */ 077 @Nonnull 078 default T resolveResourceIdentityPid( 079 @Nonnull RequestPartitionId theRequestPartitionId, 080 @Nullable String theResourceType, 081 @Nonnull String theResourceId, 082 @Nonnull ResolveIdentityMode theMode) 083 throws ResourceNotFoundException { 084 return resolveResourceIdentity(theRequestPartitionId, theResourceType, theResourceId, theMode) 085 .getPersistentId(); 086 } 087 088 /** 089 * Given a collection of resource IDs, resolve the resource identities, including the persistent ID, 090 * deleted status, resource type, etc. 091 * 092 * @since 8.0.0 093 */ 094 @Nonnull 095 Map<IIdType, IResourceLookup<T>> resolveResourceIdentities( 096 @Nonnull RequestPartitionId theRequestPartitionId, Collection<IIdType> theIds, ResolveIdentityMode theMode); 097 098 /** 099 * Given a collection of resource IDs, resolve the resource persistent IDs. 100 * 101 * @since 8.0.0 102 */ 103 default List<T> resolveResourcePids( 104 RequestPartitionId theRequestPartitionId, 105 List<IIdType> theTargetIds, 106 ResolveIdentityMode theResolveIdentityMode) { 107 return resolveResourceIdentities(theRequestPartitionId, theTargetIds, theResolveIdentityMode).values().stream() 108 .map(IResourceLookup::getPersistentId) 109 .collect(Collectors.toList()); 110 } 111 112 /** 113 * Returns true if the given resource ID should be stored in a forced ID. Under default config 114 * (meaning client ID strategy is {@link JpaStorageSettings.ClientIdStrategyEnum#ALPHANUMERIC}) 115 * this will return true if the ID has any non-digit characters. 116 * <p> 117 * In {@link JpaStorageSettings.ClientIdStrategyEnum#ANY} mode it will always return true. 118 */ 119 boolean idRequiresForcedId(String theId); 120 121 /** 122 * Value will be an empty Optional if the PID doesn't exist, or 123 * a typed resource ID if so (Patient/ABC). 124 */ 125 Optional<String> translatePidIdToForcedIdWithCache(T theResourcePersistentId); 126 127 /** 128 * Values in the returned map are typed resource IDs (Patient/ABC) 129 */ 130 PersistentIdToForcedIdMap<T> translatePidsToForcedIds(Set<T> theResourceIds); 131 132 /** 133 * This method can be called to pre-emptively add entries to the ID cache. It should 134 * be called by DAO methods if they are creating or changing the deleted status 135 * of a resource. This method returns immediately, but the data is not 136 * added to the internal caches until the current DB transaction is successfully 137 * committed, and nothing is added if the transaction rolls back. 138 */ 139 void addResolvedPidToFhirIdAfterCommit( 140 @Nonnull T theResourcePersistentId, 141 @Nonnull RequestPartitionId theRequestPartitionId, 142 @Nonnull String theResourceType, 143 @Nonnull String theFhirId, 144 @Nullable Date theDeletedAt); 145 146 @Nullable 147 T getPidOrNull(RequestPartitionId theRequestPartitionId, IBaseResource theResource); 148 149 @Nonnull 150 default T getPidOrThrowException(RequestPartitionId theRequestPartitionId, IIdType theId) { 151 IResourceLookup<T> identity = resolveResourceIdentity( 152 theRequestPartitionId, 153 theId.getResourceType(), 154 theId.getIdPart(), 155 ResolveIdentityMode.includeDeleted().cacheOk()); 156 if (identity == null) { 157 throw new InvalidRequestException(Msg.code(2295) + "Invalid ID was provided: [" + theId.getIdPart() + "]"); 158 } 159 return identity.getPersistentId(); 160 } 161 162 @Nonnull 163 T getPidOrThrowException(@Nonnull IAnyResource theResource); 164 165 IIdType resourceIdFromPidOrThrowException(T thePid, String theResourceType); 166 167 /** 168 * Given a set of PIDs, return a set of public FHIR Resource IDs. 169 * This function will resolve a forced ID if it resolves, and if it fails to resolve to a forced it, will just return the pid 170 * Example: 171 * Let's say we have Patient/1(pid == 1), Patient/pat1 (pid == 2), Patient/3 (pid == 3), their pids would resolve as follows: 172 * <p> 173 * [1,2,3] -> ["1","pat1","3"] 174 * 175 * @param thePids The Set of pids you would like to resolve to external FHIR Resource IDs. 176 * @return A Set of strings representing the FHIR IDs of the pids. 177 */ 178 Set<String> translatePidsToFhirResourceIds(Set<T> thePids); 179 180 T newPid(Object thePid); 181 182 T newPidFromStringIdAndResourceName(String thePid, String theResourceType); 183}