• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

openmrs / openmrs-core / 8240141295

11 Mar 2024 09:46PM CUT coverage: 64.693% (-0.01%) from 64.705%
8240141295

push

github

web-flow
maven(deps): bump jacksonVersion from 2.16.1 to 2.16.2 (#4582)

Bumps `jacksonVersion` from 2.16.1 to 2.16.2.

Updates `com.fasterxml.jackson.core:jackson-core` from 2.16.1 to 2.16.2
- [Commits](https://github.com/FasterXML/jackson-core/compare/jackson-core-2.16.1...jackson-core-2.16.2)

Updates `com.fasterxml.jackson.core:jackson-annotations` from 2.16.1 to 2.16.2
- [Commits](https://github.com/FasterXML/jackson/commits)

Updates `com.fasterxml.jackson.core:jackson-databind` from 2.16.1 to 2.16.2
- [Commits](https://github.com/FasterXML/jackson/commits)

Updates `com.fasterxml.jackson.datatype:jackson-datatype-jsr310` from 2.16.1 to 2.16.2

---
updated-dependencies:
- dependency-name: com.fasterxml.jackson.core:jackson-core
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: com.fasterxml.jackson.core:jackson-annotations
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: com.fasterxml.jackson.core:jackson-databind
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: com.fasterxml.jackson.datatype:jackson-datatype-jsr310
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

22763 of 35186 relevant lines covered (64.69%)

0.65 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.78
/api/src/main/java/org/openmrs/validator/ConceptValidator.java
1
/**
2
 * This Source Code Form is subject to the terms of the Mozilla Public License,
3
 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
4
 * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
5
 * the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
6
 *
7
 * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS
8
 * graphic logo is a trademark of OpenMRS Inc.
9
 */
10
package org.openmrs.validator;
11

12
import java.util.Collection;
13
import java.util.HashSet;
14
import java.util.Locale;
15
import java.util.Set;
16

17
import org.apache.commons.collections.CollectionUtils;
18
import org.apache.commons.lang3.StringUtils;
19
import org.openmrs.Concept;
20
import org.openmrs.ConceptAnswer;
21
import org.openmrs.ConceptMap;
22
import org.openmrs.ConceptName;
23
import org.openmrs.annotation.Handler;
24
import org.openmrs.api.APIException;
25
import org.openmrs.api.DuplicateConceptNameException;
26
import org.openmrs.api.context.Context;
27
import org.slf4j.Logger;
28
import org.slf4j.LoggerFactory;
29
import org.springframework.validation.Errors;
30
import org.springframework.validation.ValidationUtils;
31
import org.springframework.validation.Validator;
32

33
/**
34
 * Validates {@link Concept} objects. <br>
35
 * These validations are also documented at <a
36
 * href="https://wiki.openmrs.org/x/-gkdAg">https://wiki.openmrs.org/x/-gkdAg</a>. Any changes made
37
 * to this source also need to be reflected on that page.
38
 */
39
@Handler(supports = { Concept.class }, order = 50)
40
public class ConceptValidator extends BaseCustomizableValidator implements Validator {
1✔
41
        
42
        // Logger for this class
43
        private static final Logger log = LoggerFactory.getLogger(ConceptValidator.class);
1✔
44
        
45
        /**
46
         * Determines if the command object being submitted is a valid type
47
         *
48
         * @see org.springframework.validation.Validator#supports(java.lang.Class)
49
         */
50
        @Override
51
        public boolean supports(Class<?> c) {
52
                return Concept.class.isAssignableFrom(c);
1✔
53
        }
54
        
55
        /**
56
         * Checks that a given concept object is valid.
57
         *
58
         * @see org.springframework.validation.Validator#validate(java.lang.Object,
59
         *      org.springframework.validation.Errors)
60
         * <strong>Should</strong> pass if the concept has at least one fully specified name added to it
61
         * <strong>Should</strong> fail if there is a duplicate unretired concept name in the locale
62
         * <strong>Should</strong> fail if there is a duplicate unretired preferred name in the same locale
63
         * <strong>Should</strong> fail if there is a duplicate unretired fully specified name in the same locale
64
         * <strong>Should</strong> fail if any names in the same locale for this concept are similar
65
         * <strong>Should</strong> pass if the concept with a duplicate name is retired
66
         * <strong>Should</strong> pass if the concept being validated is retired and has a duplicate name
67
         * <strong>Should</strong> fail if any name is an empty string
68
         * <strong>Should</strong> fail if the object parameter is null
69
         * <strong>Should</strong> pass if the concept is being updated with no name change
70
         * <strong>Should</strong> fail if any name is a null value
71
         * <strong>Should</strong> not allow multiple preferred names in a given locale
72
         * <strong>Should</strong> not allow multiple fully specified conceptNames in a given locale
73
         * <strong>Should</strong> not allow multiple short names in a given locale
74
         * <strong>Should</strong> not allow an index term to be a locale preferred name
75
         * <strong>Should</strong> fail if there is no name explicitly marked as fully specified
76
         * <strong>Should</strong> pass if the duplicate ConceptName is neither preferred nor fully Specified
77
         * <strong>Should</strong> pass if the concept has a synonym that is also a short name
78
         * <strong>Should</strong> fail if a term is mapped multiple times to the same concept
79
         * <strong>Should</strong> not fail if a term has two new mappings on it
80
         * <strong>Should</strong> fail if there is a duplicate unretired concept name in the same locale different than
81
         *         the system locale
82
         * <strong>Should</strong> pass for a new concept with a map created with deprecated concept map methods
83
         * <strong>Should</strong> pass for an edited concept with a map created with deprecated concept map methods
84
         * <strong>Should</strong> pass validation if field lengths are correct
85
         * <strong>Should</strong> fail validation if field lengths are not correct
86
         * <strong>Should</strong> pass if fully specified name is the same as short name
87
         * <strong>Should</strong> pass if different concepts have the same short name
88
         * <strong>Should</strong> fail if the concept datatype is null
89
         * <strong>Should</strong> fail if the concept class is null
90
         */
91
        @Override
92
        public void validate(Object obj, Errors errors) throws APIException, DuplicateConceptNameException {
93
                
94
                if (obj == null || !(obj instanceof Concept)) {
1✔
95
                        throw new IllegalArgumentException("The parameter obj should not be null and must be of type" + Concept.class);
1✔
96
                }
97
                
98
                Concept conceptToValidate = (Concept) obj;
1✔
99
                //no name to validate, but why is this the case?
100
                if (conceptToValidate.getNames().isEmpty()) {
1✔
101
                        errors.reject("Concept.name.atLeastOneRequired");
1✔
102
                        return;
1✔
103
                }
104

105
                ValidationUtils.rejectIfEmpty(errors, "datatype", "Concept.datatype.empty");
1✔
106
                ValidationUtils.rejectIfEmpty(errors, "conceptClass", "Concept.conceptClass.empty");
1✔
107

108
                boolean hasFullySpecifiedName = false;
1✔
109
                for (Locale conceptNameLocale : conceptToValidate.getAllConceptNameLocales()) {
1✔
110
                        boolean fullySpecifiedNameForLocaleFound = false;
1✔
111
                        boolean preferredNameForLocaleFound = false;
1✔
112
                        boolean shortNameForLocaleFound = false;
1✔
113
                        Set<String> validNamesFoundInLocale = new HashSet<>();
1✔
114
                        Collection<ConceptName> namesInLocale = conceptToValidate.getNames(conceptNameLocale);
1✔
115
                        for (ConceptName nameInLocale : namesInLocale) {
1✔
116
                                if (StringUtils.isBlank(nameInLocale.getName())) {
1✔
117
                                        log.debug("Name in locale '" + conceptNameLocale.toString()
1✔
118
                                                + "' cannot be an empty string or white space");
119
                                        errors.reject("Concept.name.empty");
1✔
120
                                }
121
                                if (nameInLocale.getLocalePreferred() != null) {
1✔
122
                                        if (nameInLocale.getLocalePreferred() && !preferredNameForLocaleFound) {
1✔
123
                                                if (nameInLocale.isIndexTerm()) {
1✔
124
                                                        log.warn("Preferred name in locale '" + conceptNameLocale.toString()
×
125
                                                                + "' shouldn't be an index term");
126
                                                        errors.reject("Concept.error.preferredName.is.indexTerm");
×
127
                                                } else if (nameInLocale.isShort()) {
1✔
128
                                                        log.warn("Preferred name in locale '" + conceptNameLocale.toString()
×
129
                                                                + "' shouldn't be a short name");
130
                                                        errors.reject("Concept.error.preferredName.is.shortName");
×
131
                                                } else if (nameInLocale.getVoided()) {
1✔
132
                                                        log.warn("Preferred name in locale '" + conceptNameLocale.toString()
×
133
                                                                + "' shouldn't be a voided name");
134
                                                        errors.reject("Concept.error.preferredName.is.voided");
×
135
                                                }
136
                                                
137
                                                preferredNameForLocaleFound = true;
1✔
138
                                        }
139
                                        //should have one preferred name per locale
140
                                        else if (nameInLocale.getLocalePreferred() && preferredNameForLocaleFound) {
1✔
141
                                                log.warn("Found multiple preferred names in locale '" + conceptNameLocale.toString() + "'");
×
142
                                                errors.reject("Concept.error.multipleLocalePreferredNames");
×
143
                                        }
144
                                }
145
                                
146
                                if (nameInLocale.isFullySpecifiedName()) {
1✔
147
                                        if (!hasFullySpecifiedName) {
1✔
148
                                                hasFullySpecifiedName = true;
1✔
149
                                        }
150
                                        if (!fullySpecifiedNameForLocaleFound) {
1✔
151
                                                fullySpecifiedNameForLocaleFound = true;
1✔
152
                                        } else {
153
                                                log.warn("Found multiple fully specified names in locale '" + conceptNameLocale.toString() + "'");
×
154
                                                errors.reject("Concept.error.multipleFullySpecifiedNames");
×
155
                                        }
156
                                        if (nameInLocale.getVoided()) {
1✔
157
                                                log.warn("Fully Specified name in locale '" + conceptNameLocale.toString()
×
158
                                                        + "' shouldn't be a voided name");
159
                                                errors.reject("Concept.error.fullySpecifiedName.is.voided");
×
160
                                        }
161
                                }
162
                                
163
                                if (nameInLocale.isShort()) {
1✔
164
                                        if (!shortNameForLocaleFound) {
1✔
165
                                                shortNameForLocaleFound = true;
1✔
166
                                        }
167
                                        //should have one short name per locale
168
                                        else {
169
                                                log.warn("Found multiple short names in locale '" + conceptNameLocale.toString() + "'");
×
170
                                                errors.reject("Concept.error.multipleShortNames");
×
171
                                        }
172
                                }
173
                                
174
                                //find duplicate names for a non-retired concept
175
                                if (Context.getConceptService().isConceptNameDuplicate(nameInLocale)) {
1✔
176
                                        throw new DuplicateConceptNameException("'" + nameInLocale.getName()
1✔
177
                                                + "' is a duplicate name in locale '" + conceptNameLocale.toString() + "'");
1✔
178
                                }
179
                                
180
                                //
181
                                if (errors.hasErrors()) {
1✔
182
                                        log.debug("Concept name '" + nameInLocale.getName() + "' for locale '" + conceptNameLocale
1✔
183
                                                + "' is invalid");
184
                                        //if validation fails for any conceptName in current locale, don't proceed
185
                                        //This helps not to have multiple messages shown that are identical though they might be
186
                                        //for different conceptNames
187
                                        return;
1✔
188
                                }
189
                                
190
                                //No duplicate names allowed for the same locale and concept, keep the case the same
191
                                //except for short names
192
                                if (!nameInLocale.isShort() && !validNamesFoundInLocale.add(nameInLocale.getName().toLowerCase())) {
1✔
193
                                        throw new DuplicateConceptNameException("'" + nameInLocale.getName()
1✔
194
                                                + "' is a duplicate name in locale '" + conceptNameLocale.toString() + "' for the same concept");
1✔
195
                                }
196
                                
197
                                log.debug("Valid name found: {}", nameInLocale.getName());
1✔
198
                        }
1✔
199
                }
1✔
200
                
201
                //Ensure that each concept has at least a fully specified name
202
                if (!hasFullySpecifiedName) {
1✔
203
                        log.debug("Concept has no fully specified name");
1✔
204
                        errors.reject("Concept.error.no.FullySpecifiedName");
1✔
205
                }
206
                
207
                if (CollectionUtils.isNotEmpty(conceptToValidate.getConceptMappings())) {
1✔
208
                        //validate all the concept maps
209
                        int index = 0;
1✔
210
                        Set<Integer> mappedTermIds = null;
1✔
211
                        for (ConceptMap map : conceptToValidate.getConceptMappings()) {
1✔
212
                                if (map.getConceptReferenceTerm().getConceptReferenceTermId() == null) {
1✔
213
                                        //if this term is getting created on the fly e.g. from old legacy code, validate it
214
                                        try {
215
                                                errors.pushNestedPath("conceptMappings[" + index + "].conceptReferenceTerm");
1✔
216
                                                ValidationUtils.invokeValidator(new ConceptReferenceTermValidator(), map.getConceptReferenceTerm(),
1✔
217
                                                    errors);
218
                                        }
219
                                        finally {
220
                                                errors.popNestedPath();
1✔
221
                                        }
222
                                        
223
                                }
224

225
                                //don't proceed to the next maps since the current one already has errors
226
                                if (errors.hasErrors()) {
1✔
227
                                        return;
1✔
228
                                }
229
                                
230
                                if (mappedTermIds == null) {
1✔
231
                                        mappedTermIds = new HashSet<>();
1✔
232
                                }
233
                                
234
                                //if we already have a mapping to this term, reject it this map
235
                                if (map.getConceptReferenceTerm().getId() != null
1✔
236
                                        && !mappedTermIds.add(map.getConceptReferenceTerm().getId())) {
1✔
237
                                        errors.rejectValue("conceptMappings[" + index + "]", "ConceptReferenceTerm.term.alreadyMapped",
1✔
238
                                            "Cannot map a reference term multiple times to the same concept");
239
                                }
240
                                
241
                                index++;
1✔
242
                        }
1✔
243
                }
244
                if (CollectionUtils.isNotEmpty(conceptToValidate.getAnswers())) {
1✔
245
                        for (ConceptAnswer conceptAnswer : conceptToValidate.getAnswers()) {
1✔
246
                                if (conceptAnswer.getAnswerConcept().equals(conceptToValidate)) {
1✔
247
                                        errors.reject("Concept.contains.itself.as.answer");
1✔
248
                                }
249
                        }
1✔
250
                }
251
                ValidateUtil.validateFieldLengths(errors, obj.getClass(), "version", "retireReason");
1✔
252
                super.validateAttributes(conceptToValidate, errors, Context.getConceptService().getAllConceptAttributeTypes());
1✔
253
        }
1✔
254
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc