001/*-
002 * #%L
003 * HAPI FHIR Subscription Server
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.subscription.submit.interceptor.validator;
021
022import ca.uhn.fhir.i18n.Msg;
023import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription;
024import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscriptionChannelType;
025import ca.uhn.fhir.rest.api.EncodingEnum;
026import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
027import jakarta.annotation.Nonnull;
028
029import static org.apache.commons.lang3.StringUtils.isBlank;
030
031/**
032 *
033 * Definition of a REST Hook channel validator that perform checks on the channel payload and endpoint URL.
034 *
035 * The channel payload will always evaluate in the same manner where endpoint URL validation can be extended beyond the
036 * minimal validation perform by this class.
037 *
038 * At a minimum, this class ensures that the provided URL is not blank or null.  Supplemental validation(s) should be
039 * encapsulated into a {@link IEndpointUrlValidationStrategy} and provided with the arg constructor.
040 *
041 */
042public class RestHookChannelValidator implements IChannelTypeValidator {
043
044        private final IEndpointUrlValidationStrategy myEndpointUrlValidationStrategy;
045
046        /**
047         * Constructor for a validator where the endpoint URL will
048         */
049        public RestHookChannelValidator() {
050                this(noOpEndpointUrlValidationStrategy);
051        }
052
053        public RestHookChannelValidator(@Nonnull IEndpointUrlValidationStrategy theEndpointUrlValidationStrategy) {
054                myEndpointUrlValidationStrategy = theEndpointUrlValidationStrategy;
055        }
056
057        @Override
058        public void validateChannelType(CanonicalSubscription theSubscription) {
059                validateChannelPayload(theSubscription);
060                validateChannelEndpoint(theSubscription);
061        }
062
063        @Override
064        public CanonicalSubscriptionChannelType getSubscriptionChannelType() {
065                return CanonicalSubscriptionChannelType.RESTHOOK;
066        }
067
068        protected void validateChannelEndpoint(@Nonnull CanonicalSubscription theCanonicalSubscription) {
069                String endpointUrl = theCanonicalSubscription.getEndpointUrl();
070
071                if (isBlank(endpointUrl)) {
072                        throw new UnprocessableEntityException(
073                                        Msg.code(21) + "Rest-hook subscriptions must have Subscription.channel.endpoint defined");
074                }
075
076                myEndpointUrlValidationStrategy.validateEndpointUrl(endpointUrl);
077        }
078
079        protected void validateChannelPayload(CanonicalSubscription theResource) {
080                if (!isBlank(theResource.getPayloadString())
081                                && EncodingEnum.forContentType(theResource.getPayloadString()) == null) {
082                        throw new UnprocessableEntityException(Msg.code(1985) + "Invalid value for Subscription.channel.payload: "
083                                        + theResource.getPayloadString());
084                }
085        }
086
087        /**
088         * A concrete instantiation of this interface should provide tailored validation of an endpoint URL
089         * throwing {@link RuntimeException} upon validation failure.
090         */
091        public interface IEndpointUrlValidationStrategy {
092                void validateEndpointUrl(String theEndpointUrl);
093        }
094
095        public static final IEndpointUrlValidationStrategy noOpEndpointUrlValidationStrategy = theEndpointUrl -> {};
096}