001/*-
002 * #%L
003 * Smile CDR - CDR
004 * %%
005 * Copyright (C) 2016 - 2024 Smile CDR, Inc.
006 * %%
007 * All rights reserved.
008 * #L%
009 */
010package ca.cdr.api.fhirgw.model;
011
012import ca.uhn.fhir.model.api.Include;
013import ca.uhn.fhir.rest.api.SearchStyleEnum;
014import ca.uhn.fhir.rest.api.SearchTotalModeEnum;
015import ca.uhn.fhir.rest.api.SortSpec;
016import ca.uhn.fhir.rest.param.DateRangeParam;
017import ca.uhn.fhir.rest.param.UriAndListParam;
018import ca.uhn.fhir.rest.param.UriOrListParam;
019import com.google.common.collect.ArrayListMultimap;
020import com.google.common.collect.ListMultimap;
021import com.google.common.collect.Multimap;
022import com.google.common.collect.Multimaps;
023import jakarta.annotation.Nonnull;
024import jakarta.annotation.Nullable;
025import org.apache.commons.lang3.Validate;
026
027import java.util.Collections;
028import java.util.HashSet;
029import java.util.List;
030import java.util.Map;
031import java.util.Set;
032
033import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
034
035/**
036 * This class represents a FHIR Gateway Search request
037 */
038public class SearchRequest extends BaseRequest<SearchRequest> {
039
040        private ListMultimap<String, String> myParameters = EMPTY_STRING_MULTIMAP;
041        private Set<Include> myIncludes = Collections.emptySet();
042        private Set<Include> myRevIncludes = Collections.emptySet();
043        private SortSpec mySort;
044        private Integer myCount;
045        private SearchTotalModeEnum myTotalMode;
046        private Integer myOffset;
047        private DateRangeParam myLastUpdated;
048        private UriAndListParam myProfile;
049
050        @Nullable
051        private SearchStyleEnum mySearchStyle;
052
053        /**
054         * Constructor
055         */
056        public SearchRequest() {
057                // nothing
058        }
059
060        /**
061         * Copy constructor
062         */
063        public SearchRequest(SearchRequest theSearchRequest) {
064                super(theSearchRequest);
065
066                if (!theSearchRequest.getParameters().isEmpty()) {
067                        myParameters = ArrayListMultimap.create();
068                        myParameters.putAll(theSearchRequest.getParameters());
069                }
070
071                if (!theSearchRequest.getIncludes().isEmpty()) {
072                        myIncludes = new HashSet<>();
073                        for (Include next : theSearchRequest.getIncludes()) {
074                                myIncludes.add(next.toLocked());
075                        }
076                }
077
078                if (!theSearchRequest.getRevIncludes().isEmpty()) {
079                        myRevIncludes = new HashSet<>();
080                        for (Include next : theSearchRequest.getRevIncludes()) {
081                                myRevIncludes.add(next.toLocked());
082                        }
083                }
084
085                if (theSearchRequest.getSort() != null) {
086                        mySort = clone(theSearchRequest.getSort());
087                }
088
089                if (theSearchRequest.getLastUpdated() != null) {
090                        myLastUpdated = new DateRangeParam(
091                                        theSearchRequest.getLastUpdated().getLowerBound() != null
092                                                        ? theSearchRequest.getLastUpdated().getLowerBound().getValueAsString()
093                                                        : null,
094                                        theSearchRequest.getLastUpdated().getUpperBound() != null
095                                                        ? theSearchRequest.getLastUpdated().getUpperBound().getValueAsString()
096                                                        : null);
097                }
098
099                if (theSearchRequest.getProfile() != null) {
100                        myProfile = new UriAndListParam();
101                        for (UriOrListParam uriOrListParam : theSearchRequest.getProfile().getValuesAsQueryTokens()) {
102                                myProfile.addAnd(uriOrListParam);
103                        }
104                }
105
106                myCount = theSearchRequest.getCount();
107                myOffset = theSearchRequest.getOffset();
108                myTotalMode = theSearchRequest.getTotalMode();
109        }
110
111        private static SortSpec clone(SortSpec theSort) {
112                if (theSort == null) {
113                        return null;
114                }
115                return new SortSpec(theSort.getParamName(), theSort.getOrder(), clone(theSort.getChain()));
116        }
117
118        /**
119         * Specifies the search form (GET vs POST) to use for this search. If set to
120         * <code>null</code>, the default will be used.
121         */
122        @Nullable
123        public SearchStyleEnum getSearchStyle() {
124                return mySearchStyle;
125        }
126
127        /**
128         * Specifies the search form (GET vs POST) to use for this search. If set to
129         * <code>null</code>, the default will be used.
130         */
131        public void setSearchStyle(@Nullable SearchStyleEnum theSearchStyle) {
132                mySearchStyle = theSearchStyle;
133        }
134
135        public Multimap<String, String> getParameters() {
136                return Multimaps.unmodifiableMultimap(myParameters);
137        }
138
139        public SearchRequest setParameters(Map<String, List<String>> theParameters) {
140                Validate.notNull(theParameters, "theParameters must not be null");
141                myParameters = ArrayListMultimap.create();
142                for (Map.Entry<String, List<String>> next : theParameters.entrySet()) {
143                        myParameters.putAll(next.getKey(), next.getValue());
144                }
145                return this;
146        }
147
148        /**
149         * Remove all parameters with the given name
150         *
151         * @return Returns any parameter values that were removed
152         */
153        public List<String> removeParameters(String theName) {
154                if (!myParameters.isEmpty()) {
155                        return Collections.unmodifiableList(myParameters.removeAll(theName));
156                } else {
157                        return Collections.emptyList();
158                }
159        }
160
161        /**
162         * Remove the parameter with the given name and value, if found. Any other parameters with the same name are kept.
163         *
164         * @param theName  The parameter name
165         * @param theValue The parameter value
166         * @return Returns <code>true</code> if a parameter value was removed
167         */
168        public boolean removeParameter(String theName, String theValue) {
169                return myParameters.remove(theName, theValue);
170        }
171
172        /**
173         * Returns the first parameter value for a given parameter name (returns <code>null</code> if no parameters match the given name)
174         *
175         * @param theName The parameter name
176         * @return The value, or <code>null</code>
177         */
178        @Nullable
179        public String getParameter(String theName) {
180                List<String> values = getParameters(theName);
181                if (!values.isEmpty()) {
182                        return values.get(0);
183                }
184                return null;
185        }
186
187        /**
188         * Returns a List of parameter values for the given name. Will not return <code>null</code>.
189         *
190         * @param theName The parameter name
191         * @return A List of values, or an empty List
192         */
193        @Nonnull
194        public List<String> getParameters(String theName) {
195                List<String> values = myParameters.get(theName);
196                values = defaultIfNull(values, Collections.emptyList());
197                return Collections.unmodifiableList(values);
198        }
199
200        public Set<Include> getIncludes() {
201                return myIncludes;
202        }
203
204        public SearchRequest setIncludes(Set<Include> theIncludes) {
205                Validate.notNull(theIncludes, "theIncludes must not be null");
206                myIncludes = theIncludes;
207                return this;
208        }
209
210        public Set<Include> getRevIncludes() {
211                return myRevIncludes;
212        }
213
214        public SearchRequest setRevIncludes(Set<Include> theRevIncludes) {
215                Validate.notNull(theRevIncludes, "theRevIncludes must not be null");
216                myRevIncludes = theRevIncludes;
217                return this;
218        }
219
220        public SortSpec getSort() {
221                return mySort;
222        }
223
224        public SearchRequest setSort(SortSpec theSort) {
225                mySort = theSort;
226                return this;
227        }
228
229        public Integer getCount() {
230                return myCount;
231        }
232
233        public SearchRequest setCount(Integer theCount) {
234                myCount = theCount;
235                return this;
236        }
237
238        public Integer getOffset() {
239                return myOffset;
240        }
241
242        public SearchRequest setOffset(Integer theOffset) {
243                myOffset = theOffset;
244                return this;
245        }
246
247        public DateRangeParam getLastUpdated() {
248                return myLastUpdated;
249        }
250
251        public SearchRequest setLastUpdated(DateRangeParam theLastUpdated) {
252                myLastUpdated = theLastUpdated;
253                return this;
254        }
255
256        public UriAndListParam getProfile() {
257                return myProfile;
258        }
259
260        public SearchRequest setProfile(UriAndListParam theProfile) {
261                myProfile = theProfile;
262                return this;
263        }
264
265        public SearchTotalModeEnum getTotalMode() {
266                return myTotalMode;
267        }
268
269        public SearchRequest setTotalMode(SearchTotalModeEnum theTotalMode) {
270                myTotalMode = theTotalMode;
271                return this;
272        }
273
274        /**
275         * Add a new search parameter
276         */
277        public void addParameter(String theName, String theValue) {
278                Validate.notBlank(theName, "theName must not be empty");
279                Validate.notBlank(theValue, "theValue must not be empty");
280                if (myParameters.isEmpty()) {
281                        myParameters = ArrayListMultimap.create();
282                }
283                myParameters.put(theName, theValue);
284        }
285}