001/*
002 * #%L
003 * HAPI FHIR - Client 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.client.method;
021
022import ca.uhn.fhir.context.ConfigurationException;
023import ca.uhn.fhir.context.FhirContext;
024import ca.uhn.fhir.i18n.Msg;
025import ca.uhn.fhir.model.primitive.IdDt;
026import ca.uhn.fhir.model.valueset.BundleTypeEnum;
027import ca.uhn.fhir.rest.annotation.Elements;
028import ca.uhn.fhir.rest.annotation.IdParam;
029import ca.uhn.fhir.rest.annotation.Read;
030import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
031import ca.uhn.fhir.rest.param.ParameterUtil;
032import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
033import org.apache.commons.io.IOUtils;
034import org.apache.commons.lang3.Validate;
035import org.hl7.fhir.instance.model.api.IBaseBinary;
036import org.hl7.fhir.instance.model.api.IBaseResource;
037import org.hl7.fhir.instance.model.api.IIdType;
038
039import java.io.IOException;
040import java.io.InputStream;
041import java.lang.reflect.Method;
042import java.util.ArrayList;
043import java.util.Collections;
044import java.util.List;
045import java.util.Map;
046
047public class ReadMethodBinding extends BaseResourceReturningMethodBinding
048                implements IClientResponseHandlerHandlesBinary<Object> {
049        private Integer myIdIndex;
050        private boolean mySupportsVersion;
051        private Class<? extends IIdType> myIdParameterType;
052
053        @SuppressWarnings("unchecked")
054        public ReadMethodBinding(
055                        Class<? extends IBaseResource> theAnnotatedResourceType,
056                        Method theMethod,
057                        FhirContext theContext,
058                        Object theProvider) {
059                super(theAnnotatedResourceType, theMethod, theContext, theProvider);
060
061                Validate.notNull(theMethod, "Method must not be null");
062
063                Integer idIndex = ParameterUtil.findIdParameterIndex(theMethod, getContext());
064
065                Class<?>[] parameterTypes = theMethod.getParameterTypes();
066
067                mySupportsVersion = theMethod.getAnnotation(Read.class).version();
068                myIdIndex = idIndex;
069
070                if (myIdIndex == null) {
071                        throw new ConfigurationException(
072                                        Msg.code(1423) + "@" + Read.class.getSimpleName() + " method " + theMethod.getName() + " on type \""
073                                                        + theMethod.getDeclaringClass().getName() + "\" does not have a parameter annotated with @"
074                                                        + IdParam.class.getSimpleName());
075                }
076                myIdParameterType = (Class<? extends IIdType>) parameterTypes[myIdIndex];
077
078                if (!IIdType.class.isAssignableFrom(myIdParameterType)) {
079                        throw new ConfigurationException(
080                                        Msg.code(1424) + "ID parameter must be of type IdDt or IdType - Found: " + myIdParameterType);
081                }
082        }
083
084        @Override
085        public List<Class<?>> getAllowableParamAnnotations() {
086                ArrayList<Class<?>> retVal = new ArrayList<Class<?>>();
087                retVal.add(IdParam.class);
088                retVal.add(Elements.class);
089                return retVal;
090        }
091
092        @Override
093        public RestOperationTypeEnum getRestOperationType() {
094                return isVread() ? RestOperationTypeEnum.VREAD : RestOperationTypeEnum.READ;
095        }
096
097        @Override
098        public ReturnTypeEnum getReturnType() {
099                return ReturnTypeEnum.RESOURCE;
100        }
101
102        @Override
103        public HttpGetClientInvocation invokeClient(Object[] theArgs) {
104                HttpGetClientInvocation retVal;
105                IIdType id = ((IIdType) theArgs[myIdIndex]);
106                String resourceName = getResourceName();
107                if (id.hasVersionIdPart()) {
108                        retVal = createVReadInvocation(
109                                        getContext(), new IdDt(resourceName, id.getIdPart(), id.getVersionIdPart()), resourceName);
110                } else {
111                        retVal = createReadInvocation(getContext(), id, resourceName);
112                }
113
114                for (int idx = 0; idx < theArgs.length; idx++) {
115                        IParameter nextParam = getParameters().get(idx);
116                        nextParam.translateClientArgumentIntoQueryArgument(getContext(), theArgs[idx], null, null);
117                }
118
119                return retVal;
120        }
121
122        @Override
123        public Object invokeClientForBinary(
124                        String theResponseMimeType,
125                        InputStream theResponseReader,
126                        int theResponseStatusCode,
127                        Map<String, List<String>> theHeaders)
128                        throws IOException, BaseServerResponseException {
129                byte[] contents = IOUtils.toByteArray(theResponseReader);
130
131                IBaseBinary resource =
132                                (IBaseBinary) getContext().getResourceDefinition("Binary").newInstance();
133                resource.setContentType(theResponseMimeType);
134                resource.setContent(contents);
135
136                switch (getMethodReturnType()) {
137                        case LIST_OF_RESOURCES:
138                                return Collections.singletonList(resource);
139                        case RESOURCE:
140                                return resource;
141                        case BUNDLE_RESOURCE:
142                        case METHOD_OUTCOME:
143                                break;
144                }
145
146                throw new IllegalStateException(Msg.code(1425) + "" + getMethodReturnType()); // should not happen
147        }
148
149        @Override
150        public boolean isBinary() {
151                return "Binary".equals(getResourceName());
152        }
153
154        public boolean isVread() {
155                return mySupportsVersion;
156        }
157
158        public static HttpGetClientInvocation createAbsoluteReadInvocation(FhirContext theContext, IIdType theId) {
159                return new HttpGetClientInvocation(theContext, theId.toVersionless().getValue());
160        }
161
162        public static HttpGetClientInvocation createAbsoluteVReadInvocation(FhirContext theContext, IIdType theId) {
163                return new HttpGetClientInvocation(theContext, theId.getValue());
164        }
165
166        public static HttpGetClientInvocation createReadInvocation(
167                        FhirContext theContext, IIdType theId, String theResourceName) {
168                return new HttpGetClientInvocation(theContext, new IdDt(theResourceName, theId.getIdPart()).getValue());
169        }
170
171        public static HttpGetClientInvocation createVReadInvocation(
172                        FhirContext theContext, IIdType theId, String theResourceName) {
173                return new HttpGetClientInvocation(
174                                theContext, new IdDt(theResourceName, theId.getIdPart(), theId.getVersionIdPart()).getValue());
175        }
176
177        @Override
178        protected BundleTypeEnum getResponseBundleType() {
179                return null;
180        }
181}