001/*- 002 * #%L 003 * Smile CDR - CDR 004 * %% 005 * Copyright (C) 2016 - 2025 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 mySearchStyle = theSearchRequest.getSearchStyle(); 110 } 111 112 private static SortSpec clone(SortSpec theSort) { 113 if (theSort == null) { 114 return null; 115 } 116 return new SortSpec(theSort.getParamName(), theSort.getOrder(), clone(theSort.getChain())); 117 } 118 119 /** 120 * Specifies the search form (GET vs POST) to use for this search. If set to 121 * <code>null</code>, the default will be used. 122 */ 123 @Nullable 124 public SearchStyleEnum getSearchStyle() { 125 return mySearchStyle; 126 } 127 128 /** 129 * Specifies the search form (GET vs POST) to use for this search. If set to 130 * <code>null</code>, the default will be used. 131 */ 132 public void setSearchStyle(@Nullable SearchStyleEnum theSearchStyle) { 133 mySearchStyle = theSearchStyle; 134 } 135 136 public Multimap<String, String> getParameters() { 137 return Multimaps.unmodifiableMultimap(myParameters); 138 } 139 140 public SearchRequest setParameters(Map<String, List<String>> theParameters) { 141 Validate.notNull(theParameters, "theParameters must not be null"); 142 myParameters = ArrayListMultimap.create(); 143 for (Map.Entry<String, List<String>> next : theParameters.entrySet()) { 144 myParameters.putAll(next.getKey(), next.getValue()); 145 } 146 return this; 147 } 148 149 /** 150 * Remove all parameters with the given name 151 * 152 * @return Returns any parameter values that were removed 153 */ 154 public List<String> removeParameters(String theName) { 155 if (!myParameters.isEmpty()) { 156 return Collections.unmodifiableList(myParameters.removeAll(theName)); 157 } else { 158 return Collections.emptyList(); 159 } 160 } 161 162 /** 163 * Remove the parameter with the given name and value, if found. Any other parameters with the same name are kept. 164 * 165 * @param theName The parameter name 166 * @param theValue The parameter value 167 * @return Returns <code>true</code> if a parameter value was removed 168 */ 169 public boolean removeParameter(String theName, String theValue) { 170 return myParameters.remove(theName, theValue); 171 } 172 173 /** 174 * Returns the first parameter value for a given parameter name (returns <code>null</code> if no parameters match the given name) 175 * 176 * @param theName The parameter name 177 * @return The value, or <code>null</code> 178 */ 179 @Nullable 180 public String getParameter(String theName) { 181 List<String> values = getParameters(theName); 182 if (!values.isEmpty()) { 183 return values.get(0); 184 } 185 return null; 186 } 187 188 /** 189 * Returns a List of parameter values for the given name. Will not return <code>null</code>. 190 * 191 * @param theName The parameter name 192 * @return A List of values, or an empty List 193 */ 194 @Nonnull 195 public List<String> getParameters(String theName) { 196 List<String> values = myParameters.get(theName); 197 values = defaultIfNull(values, Collections.emptyList()); 198 return Collections.unmodifiableList(values); 199 } 200 201 public Set<Include> getIncludes() { 202 return myIncludes; 203 } 204 205 public SearchRequest setIncludes(Set<Include> theIncludes) { 206 Validate.notNull(theIncludes, "theIncludes must not be null"); 207 myIncludes = theIncludes; 208 return this; 209 } 210 211 public Set<Include> getRevIncludes() { 212 return myRevIncludes; 213 } 214 215 public SearchRequest setRevIncludes(Set<Include> theRevIncludes) { 216 Validate.notNull(theRevIncludes, "theRevIncludes must not be null"); 217 myRevIncludes = theRevIncludes; 218 return this; 219 } 220 221 public SortSpec getSort() { 222 return mySort; 223 } 224 225 public SearchRequest setSort(SortSpec theSort) { 226 mySort = theSort; 227 return this; 228 } 229 230 public Integer getCount() { 231 return myCount; 232 } 233 234 public SearchRequest setCount(Integer theCount) { 235 myCount = theCount; 236 return this; 237 } 238 239 public Integer getOffset() { 240 return myOffset; 241 } 242 243 public SearchRequest setOffset(Integer theOffset) { 244 myOffset = theOffset; 245 return this; 246 } 247 248 public DateRangeParam getLastUpdated() { 249 return myLastUpdated; 250 } 251 252 public SearchRequest setLastUpdated(DateRangeParam theLastUpdated) { 253 myLastUpdated = theLastUpdated; 254 return this; 255 } 256 257 public UriAndListParam getProfile() { 258 return myProfile; 259 } 260 261 public SearchRequest setProfile(UriAndListParam theProfile) { 262 myProfile = theProfile; 263 return this; 264 } 265 266 public SearchTotalModeEnum getTotalMode() { 267 return myTotalMode; 268 } 269 270 public SearchRequest setTotalMode(SearchTotalModeEnum theTotalMode) { 271 myTotalMode = theTotalMode; 272 return this; 273 } 274 275 /** 276 * Add a new search parameter 277 */ 278 public void addParameter(String theName, String theValue) { 279 Validate.notBlank(theName, "theName must not be empty"); 280 Validate.notBlank(theValue, "theValue must not be empty"); 281 if (myParameters.isEmpty()) { 282 myParameters = ArrayListMultimap.create(); 283 } 284 myParameters.put(theName, theValue); 285 } 286}