001/*-
002 * #%L
003 * HAPI FHIR - Server Framework
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.rest.server.interceptor.partition;
021
022import ca.uhn.fhir.i18n.Msg;
023import ca.uhn.fhir.interceptor.api.Hook;
024import ca.uhn.fhir.interceptor.api.Interceptor;
025import ca.uhn.fhir.interceptor.api.Pointcut;
026import ca.uhn.fhir.interceptor.model.RequestPartitionId;
027import ca.uhn.fhir.rest.api.server.RequestDetails;
028import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
029import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
030import ca.uhn.fhir.rest.server.provider.ProviderConstants;
031import ca.uhn.fhir.rest.server.tenant.ITenantIdentificationStrategy;
032import jakarta.annotation.Nonnull;
033
034import static org.apache.commons.lang3.StringUtils.isBlank;
035
036/**
037 * This interceptor uses the request tenant ID (as supplied to the server using
038 * {@link ca.uhn.fhir.rest.server.RestfulServer#setTenantIdentificationStrategy(ITenantIdentificationStrategy)}
039 * to indicate the partition ID. With this interceptor registered, The server treats the tenant name
040 * supplied by the {@link ITenantIdentificationStrategy tenant identification strategy} as a partition name.
041 * <p>
042 * Partition names (aka tenant IDs) must be registered in advance using the partition management operations.
043 * </p>
044 *
045 * @since 5.0.0
046 */
047@Interceptor
048public class RequestTenantPartitionInterceptor {
049
050        @Hook(Pointcut.STORAGE_PARTITION_IDENTIFY_ANY)
051        public RequestPartitionId partitionIdentifyCreate(RequestDetails theRequestDetails) {
052                return extractPartitionIdFromRequest(theRequestDetails);
053        }
054
055        @Nonnull
056        protected RequestPartitionId extractPartitionIdFromRequest(RequestDetails theRequestDetails) {
057
058                // We will use the tenant ID that came from the request as the partition name
059                String tenantId = theRequestDetails.getTenantId();
060                if (isBlank(tenantId)) {
061                        // this branch is no-op happen when "partitioning.tenant_identification_strategy" is set to URL_BASED
062                        if (theRequestDetails instanceof SystemRequestDetails) {
063                                SystemRequestDetails requestDetails = (SystemRequestDetails) theRequestDetails;
064                                if (requestDetails.getRequestPartitionId() != null) {
065                                        return requestDetails.getRequestPartitionId();
066                                }
067                                return RequestPartitionId.defaultPartition();
068                        }
069                        throw new InternalErrorException(Msg.code(343) + "No partition ID has been specified");
070                }
071
072                // for REQUEST_TENANT partition selection mode, allPartitions is supported when URL includes _ALL as the tenant
073                // else if no tenant is provided in the URL, DEFAULT will be used as per UrlBaseTenantIdentificationStrategy
074                if (tenantId.equals(ProviderConstants.ALL_PARTITIONS_TENANT_NAME)) {
075                        return RequestPartitionId.allPartitions();
076                }
077                return RequestPartitionId.fromPartitionName(tenantId);
078        }
079}