001/*- 002 * #%L 003 * Smile CDR - CDR 004 * %% 005 * Copyright (C) 2016 - 2026 Smile CDR, Inc. 006 * %% 007 * All rights reserved. 008 * #L% 009 */ 010package ca.cdr.api.model.json; 011 012import ca.uhn.fhir.model.api.IModelJson; 013import ca.uhn.fhir.model.api.annotation.ExampleSupplier; 014import com.fasterxml.jackson.annotation.JsonProperty; 015import io.swagger.v3.oas.annotations.media.Schema; 016import jakarta.annotation.Nonnull; 017import jakarta.annotation.Nullable; 018 019import java.util.Objects; 020import java.util.function.Supplier; 021 022import static org.apache.commons.lang3.ObjectUtils.getIfNull; 023 024@SuppressWarnings("UnusedReturnValue") 025@Schema(name = "TokenizationRule", description = "Defines a single rule for FHIR Repository Tokenization") 026@ExampleSupplier(TokenizationRuleJson.ExampleSupplier.class) 027public class TokenizationRuleJson implements IModelJson, Cloneable { 028 029 @Schema( 030 description = 031 "An optional description of this rule. This field does not affect the functionality of the rule, but can be used as documentation.") 032 @JsonProperty(value = "description", required = false) 033 private String myDescription; 034 035 @Schema( 036 description = 037 "A FHIRPath expression within a resource pointing to the element to tokenize. The value must begin with a resource type, e.g. `Patient.name.family`.") 038 @JsonProperty(value = "path", required = true) 039 private String myPath; 040 041 @JsonProperty(value = "searchParameter", required = false) 042 @Schema( 043 description = 044 "The name of a SearchParameter which can be used to search for the tokenized value. If the FHIRPath expression in this rule matches the path of an existing SearchParameter, it is recommended to use the name of that SearchParameter here. This may be left empty, in which case it will not be possible to perform a search for the tokenized value (in other words, this should only be populated for rules containing paths which should be indexed for searching).") 045 private String mySearchParameter; 046 047 @JsonProperty(value = "searchValueNormalization", required = false) 048 @Schema( 049 description = 050 "If this property is set, the element value will be tokenized twice. The first value contains the original element as it was stored in the resource, and the second contains a normalized version of the element. The normalized version is used for search indexing. See [Search Normalization](/docs/fhir_repository/tokenization.html#search-normalization) for more information.") 051 private TokenizationValueNormalizationEnum mySearchValueNormalization; 052 053 @JsonProperty(value = "neverDetokenize", required = false) 054 @Schema( 055 description = 056 "If set to true, the system will never attempt to de-tokenize the value. This could be set if the configured tokenization algorithm is not reversible, or the usage scenario means that it is not desirable to reverse the tokenization on this element.") 057 private boolean myNeverDetokenize; 058 059 @JsonProperty(value = "status", required = false) 060 @Schema( 061 description = 062 "The status of this rule. If not set, defaults to ACTIVE. Setting the status to QUIESCE means that the rule is still available for detokenization but new data should not be tokenized with this rule. See [Updating Tokenization Rules](/docs/fhir_repository/tokenization.html#update-tokenization-rules) for more information.") 063 private RuleStatusEnum myStatus; 064 065 /** 066 * Constructor 067 */ 068 public TokenizationRuleJson() { 069 super(); 070 } 071 072 /** 073 * Copy Constructor 074 */ 075 public TokenizationRuleJson(TokenizationRuleJson theTokenizationRuleJson) { 076 myDescription = theTokenizationRuleJson.myDescription; 077 myPath = theTokenizationRuleJson.myPath; 078 mySearchParameter = theTokenizationRuleJson.mySearchParameter; 079 mySearchValueNormalization = theTokenizationRuleJson.mySearchValueNormalization; 080 myNeverDetokenize = theTokenizationRuleJson.myNeverDetokenize; 081 myStatus = theTokenizationRuleJson.myStatus; 082 } 083 084 /** 085 * Defaults to {@link RuleStatusEnum#ACTIVE} if not set 086 * @since 2025.11 087 */ 088 @Nonnull 089 public RuleStatusEnum getStatus() { 090 return getIfNull(myStatus, RuleStatusEnum.ACTIVE); 091 } 092 093 public TokenizationRuleJson setStatus(RuleStatusEnum theStatus) { 094 myStatus = theStatus; 095 return this; 096 } 097 098 public String getDescription() { 099 return myDescription; 100 } 101 102 public TokenizationRuleJson setDescription(String theDescription) { 103 myDescription = theDescription; 104 return this; 105 } 106 107 public String getPath() { 108 return myPath; 109 } 110 111 public TokenizationRuleJson setPath(String thePath) { 112 myPath = thePath; 113 return this; 114 } 115 116 public String getSearchParameter() { 117 return mySearchParameter; 118 } 119 120 public TokenizationRuleJson setSearchParameter(String theSearchParameter) { 121 mySearchParameter = theSearchParameter; 122 return this; 123 } 124 125 @Nullable 126 public TokenizationValueNormalizationEnum getSearchValueNormalization() { 127 return mySearchValueNormalization; 128 } 129 130 public TokenizationRuleJson setSearchValueNormalization( 131 TokenizationValueNormalizationEnum theSearchValueNormalization) { 132 mySearchValueNormalization = theSearchValueNormalization; 133 return this; 134 } 135 136 @Nullable 137 public String getResourceType() { 138 int dotIdx = myPath.indexOf('.'); 139 if (dotIdx != -1) { 140 return myPath.substring(0, dotIdx); 141 } 142 return null; 143 } 144 145 public boolean isNeverDetokenize() { 146 return myNeverDetokenize; 147 } 148 149 public TokenizationRuleJson setNeverDetokenize(boolean theNeverDetokenize) { 150 myNeverDetokenize = theNeverDetokenize; 151 return this; 152 } 153 154 @SuppressWarnings("MethodDoesntCallSuperMethod") 155 @Override 156 public TokenizationRuleJson clone() { 157 return new TokenizationRuleJson(this); 158 } 159 160 @Override 161 public boolean equals(Object theO) { 162 if (!(theO instanceof TokenizationRuleJson that)) { 163 return false; 164 } 165 return myNeverDetokenize == that.myNeverDetokenize 166 && Objects.equals(myDescription, that.myDescription) 167 && Objects.equals(myPath, that.myPath) 168 && Objects.equals(mySearchParameter, that.mySearchParameter) 169 && mySearchValueNormalization == that.mySearchValueNormalization 170 && myStatus == that.myStatus; 171 } 172 173 @Override 174 public int hashCode() { 175 return Objects.hash( 176 myDescription, myPath, mySearchParameter, mySearchValueNormalization, myNeverDetokenize, myStatus); 177 } 178 179 @Override 180 public String toString() { 181 return "TokenizationRuleJson[" + "path='" 182 + myPath + '\'' + ", description='" 183 + myDescription + '\'' + ", searchParameter='" 184 + mySearchParameter + '\'' + ", searchValueNormalization=" 185 + mySearchValueNormalization + ", neverDetokenize=" 186 + myNeverDetokenize + ", status=" 187 + myStatus + "]'"; 188 } 189 190 public enum RuleStatusEnum { 191 /** 192 * Rule is active - this is the default 193 */ 194 @Schema(description = "Rule is active - this is the default.") 195 ACTIVE, 196 197 /** 198 * Rule is being disabled: existing data stored with this rule can be detokenized, but new data 199 * should not be tokenized with this rule. 200 */ 201 @Schema( 202 description = 203 "Rule is being disabled: existing data stored with this rule can be detokenized, but new data should not be tokenized with this rule.") 204 QUIESCE, 205 206 /** 207 * Rule is completely ignored 208 */ 209 @Schema(description = "Rule is completely ignored.") 210 DISABLED 211 } 212 213 public static class ExampleSupplier implements Supplier<TokenizationRuleJson> { 214 @Override 215 public TokenizationRuleJson get() { 216 TokenizationRuleJson rule = new TokenizationRuleJson(); 217 rule.setPath("Patient.name.family"); 218 rule.setDescription("Patient family name"); 219 rule.setSearchParameter("family"); 220 rule.setSearchValueNormalization(TokenizationValueNormalizationEnum.STRING); 221 rule.setStatus(TokenizationRuleJson.RuleStatusEnum.ACTIVE); 222 return rule; 223 } 224 } 225}