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.method;
021
022import ca.uhn.fhir.context.ConfigurationException;
023import ca.uhn.fhir.i18n.Msg;
024import ca.uhn.fhir.rest.annotation.Count;
025import ca.uhn.fhir.rest.api.Constants;
026import ca.uhn.fhir.rest.api.server.RequestDetails;
027import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
028import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
029import com.fasterxml.jackson.databind.JsonNode;
030import com.fasterxml.jackson.databind.ObjectMapper;
031import org.apache.commons.io.IOUtils;
032
033import java.io.IOException;
034import java.io.Reader;
035import java.lang.reflect.Method;
036import java.util.Collection;
037
038import static ca.uhn.fhir.rest.api.Constants.CT_GRAPHQL;
039import static ca.uhn.fhir.rest.api.Constants.CT_JSON;
040import static ca.uhn.fhir.rest.server.method.ResourceParameter.createRequestReader;
041import static org.apache.commons.lang3.StringUtils.defaultString;
042import static org.apache.commons.lang3.StringUtils.trim;
043
044public class GraphQLQueryBodyParameter implements IParameter {
045
046        private Class<?> myType;
047
048        @Override
049        public Object translateQueryParametersIntoServerArgument(
050                        RequestDetails theRequest, BaseMethodBinding theMethodBinding)
051                        throws InternalErrorException, InvalidRequestException {
052                String ctValue = defaultString(theRequest.getHeader(Constants.HEADER_CONTENT_TYPE));
053                Reader requestReader = createRequestReader(theRequest);
054
055                // Trim off "; charset=FOO" from the content-type header
056                int semicolonIdx = ctValue.indexOf(';');
057                if (semicolonIdx != -1) {
058                        ctValue = ctValue.substring(0, semicolonIdx);
059                }
060                ctValue = trim(ctValue);
061
062                if (CT_JSON.equals(ctValue)) {
063                        try {
064                                ObjectMapper mapper = new ObjectMapper();
065                                JsonNode jsonNode = mapper.readTree(requestReader);
066                                if (jsonNode != null && jsonNode.get("query") != null) {
067                                        return jsonNode.get("query").asText();
068                                }
069                        } catch (IOException e) {
070                                throw new InternalErrorException(Msg.code(356) + e);
071                        }
072                }
073
074                if (CT_GRAPHQL.equals(ctValue)) {
075                        try {
076                                return IOUtils.toString(requestReader);
077                        } catch (IOException e) {
078                                throw new InternalErrorException(Msg.code(357) + e);
079                        }
080                }
081
082                return null;
083        }
084
085        @Override
086        public void initializeTypes(
087                        Method theMethod,
088                        Class<? extends Collection<?>> theOuterCollectionType,
089                        Class<? extends Collection<?>> theInnerCollectionType,
090                        Class<?> theParameterType) {
091                if (theOuterCollectionType != null) {
092                        throw new ConfigurationException(Msg.code(358) + "Method '" + theMethod.getName() + "' in type '"
093                                        + theMethod.getDeclaringClass().getCanonicalName() + "' is annotated with @" + Count.class.getName()
094                                        + " but can not be of collection type");
095                }
096                if (!String.class.equals(theParameterType)) {
097                        throw new ConfigurationException(Msg.code(359) + "Method '" + theMethod.getName() + "' in type '"
098                                        + theMethod.getDeclaringClass().getCanonicalName() + "' is annotated with @" + Count.class.getName()
099                                        + " but type '" + theParameterType + "' is an invalid type, must be one of Integer or IntegerType");
100                }
101                myType = theParameterType;
102        }
103}