Skip to content

Commit dc860e9

Browse files
committed
CMR-10801: refactors longname validation to handle not provided
1 parent d3fbc7b commit dc860e9

File tree

3 files changed

+186
-23
lines changed

3 files changed

+186
-23
lines changed

common-app-lib/src/cmr/common_app/services/kms_lookup.clj

Lines changed: 57 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,15 @@
267267
(error "lookup-by-location-string found redis carmine exception. Will return nil result." e)
268268
(throw e)))))
269269

270+
(defn- skip-long-name-validation?
271+
"Returns true if the long name should be skipped in validation.
272+
Long name is skipped if it's nil, empty string, or 'Not provided' (case-insensitive)."
273+
[long-name]
274+
(or (nil? long-name)
275+
(string/blank? long-name)
276+
(when (string? long-name)
277+
(= "not provided" (string/lower-case (string/trim long-name))))))
278+
270279
(defn- remove-long-name-from-kms-index
271280
"Removes long-name from the umm-c-index keys in order to prevent validation when
272281
long-name is not present in the umm-c-keyword. We only want to validate long-name if it is not nil."
@@ -296,30 +305,56 @@
296305
(error "lookup-by-umm-c-keyword-data-format found redis carmine exception. Will return nil result." e)
297306
(throw e)))))
298307

308+
(defn- lookup-by-umm-c-keyword-with-long-name
309+
"Generic lookup function for keyword types that have long-name field (platforms, instruments, projects).
310+
Takes a keyword as represented in the UMM concepts as a map and returns the KMS keyword map
311+
as its stored in the cache. Returns nil if a keyword is not found. Comparison is made case insensitively.
312+
The transform-fn parameter is an optional function to apply additional transformations to the keyword
313+
before lookup (e.g., renaming :type to :category for platforms)."
314+
([context keyword-scheme umm-c-keyword]
315+
(lookup-by-umm-c-keyword-with-long-name context keyword-scheme umm-c-keyword identity))
316+
([context keyword-scheme umm-c-keyword transform-fn]
317+
(try
318+
(let [umm-c-keyword (csk-extras/transform-keys csk/->kebab-case umm-c-keyword)
319+
umm-c-keyword (transform-fn umm-c-keyword)
320+
skip-long-name? (skip-long-name-validation? (get umm-c-keyword :long-name))
321+
;; If long-name should be skipped (nil, empty, or "Not provided"), remove it before normalization
322+
keyword-for-comparison (if skip-long-name? (dissoc umm-c-keyword :long-name) umm-c-keyword)
323+
comparison-map (normalize-for-lookup keyword-for-comparison (kms-scheme->fields-for-umm-c-lookup keyword-scheme))
324+
umm-c-cache (hash-cache/context->cache context kms-umm-c-cache-key)
325+
[tm value] (util/time-execution (hash-cache/get-value umm-c-cache kms-umm-c-cache-key keyword-scheme))
326+
_ (rl-util/log-redis-read-complete (str "lookup-by-umm-c-keyword-" keyword-scheme) kms-umm-c-cache-key tm)
327+
;; When skipping long-name, remove it from KMS index keys to enable matching on remaining fields
328+
value-for-lookup (if skip-long-name? (remove-long-name-from-kms-index value) value)]
329+
(get value-for-lookup comparison-map))
330+
(catch Exception e
331+
(if (clojure.string/includes? (ex-message e) "Carmine connection error")
332+
(error (str "lookup-by-umm-c-keyword-" keyword-scheme " found redis carmine exception. Will return nil result.") e)
333+
(throw e))))))
334+
299335
(defn lookup-by-umm-c-keyword-platforms
300336
"Takes a keyword as represented in the UMM concepts as a map and returns the KMS keyword map
301337
as its stored in the cache. Returns nil if a keyword is not found. Comparison is made case insensitively."
302338
[context keyword-scheme umm-c-keyword]
303-
(try
304-
(let [umm-c-keyword (csk-extras/transform-keys csk/->kebab-case umm-c-keyword)
305-
;; CMR-3696 This is needed to compare the keyword category, which is mapped
306-
;; to the UMM Platform Type field. This will avoid complications with facets.
307-
umm-c-keyword (set/rename-keys umm-c-keyword {:type :category})
308-
comparison-map (normalize-for-lookup umm-c-keyword (kms-scheme->fields-for-umm-c-lookup keyword-scheme))
309-
umm-c-cache (hash-cache/context->cache context kms-umm-c-cache-key)
310-
[tm value] (util/time-execution (hash-cache/get-value umm-c-cache kms-umm-c-cache-key keyword-scheme))
311-
_ (rl-util/log-redis-read-complete "lookup-by-umm-c-keyword-platforms" kms-umm-c-cache-key tm)]
312-
(if (get umm-c-keyword :long-name)
313-
;; Check both longname and shortname
314-
(get-in value [comparison-map])
315-
;; Check just shortname
316-
(-> value
317-
(remove-long-name-from-kms-index)
318-
(get comparison-map))))
319-
(catch Exception e
320-
(if (clojure.string/includes? (ex-message e) "Carmine connection error")
321-
(error "lookup-by-umm-c-keyword-platforms found redis carmine exception. Will return nil result." e)
322-
(throw e)))))
339+
;; CMR-3696 This is needed to compare the keyword category, which is mapped
340+
;; to the UMM Platform Type field. This will avoid complications with facets.
341+
(lookup-by-umm-c-keyword-with-long-name
342+
context
343+
keyword-scheme
344+
umm-c-keyword
345+
#(set/rename-keys % {:type :category})))
346+
347+
(defn lookup-by-umm-c-keyword-instruments
348+
"Takes a keyword as represented in the UMM concepts as a map and returns the KMS keyword map
349+
as its stored in the cache. Returns nil if a keyword is not found. Comparison is made case insensitively."
350+
[context keyword-scheme umm-c-keyword]
351+
(lookup-by-umm-c-keyword-with-long-name context keyword-scheme umm-c-keyword))
352+
353+
(defn lookup-by-umm-c-keyword-projects
354+
"Takes a keyword as represented in the UMM concepts as a map and returns the KMS keyword map
355+
as its stored in the cache. Returns nil if a keyword is not found. Comparison is made case insensitively."
356+
[context keyword-scheme umm-c-keyword]
357+
(lookup-by-umm-c-keyword-with-long-name context keyword-scheme umm-c-keyword))
323358

324359
(defn lookup-by-umm-c-keyword
325360
"Takes a keyword as represented in UMM concepts as a map and returns the KMS keyword as it exists
@@ -329,6 +364,8 @@
329364
(when-not (:ignore-kms-keywords context)
330365
(case keyword-scheme
331366
:platforms (lookup-by-umm-c-keyword-platforms context keyword-scheme umm-c-keyword)
367+
:instruments (lookup-by-umm-c-keyword-instruments context keyword-scheme umm-c-keyword)
368+
:projects (lookup-by-umm-c-keyword-projects context keyword-scheme umm-c-keyword)
332369
:granule-data-format (lookup-by-umm-c-keyword-data-format context keyword-scheme umm-c-keyword)
333370
;; default
334371
(let [umm-c-keyword (csk-extras/transform-keys csk/->kebab-case umm-c-keyword)

common-app-lib/test/cmr/common_app/test/services/kms_lookup.clj

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,12 @@
1111
{:providers [{:level-0 "ACADEMIC" :level-1 "OR-STATE/EOARC" :short-name "PROV1"
1212
:long-name "Eastern Oregon Agriculture Research Center, Oregon State University"
1313
:uuid "prov1-uuid"}]
14-
:platforms [{:short-name "PLAT1" :long-name "Platform 1" :category "Aircraft" :other-random-key 7 :uuid "plat1-uuid"}]
15-
:instruments [{:short-name "INST1" :long-name "Instrument 1" :uuid "inst1-uuid"}]
16-
:projects [{:short-name "PROJ1" :long-name "Project 1" :uuid "proj1-uuid"}]
14+
:platforms [{:short-name "PLAT1" :long-name "Platform 1" :category "Aircraft" :other-random-key 7 :uuid "plat1-uuid"}
15+
{:short-name "PLAT2" :category "Spacecraft" :uuid "plat2-uuid"}]
16+
:instruments [{:short-name "INST1" :long-name "Instrument 1" :uuid "inst1-uuid"}
17+
{:short-name "INST2" :uuid "inst2-uuid"}]
18+
:projects [{:short-name "PROJ1" :long-name "Project 1" :uuid "proj1-uuid"}
19+
{:short-name "PROJ2" :uuid "proj2-uuid"}]
1720
:spatial-keywords [{:category "CONTINENT" :type "AFRICA" :subregion-1 "CENTRAL AFRICA"
1821
:subregion-2 "CHAD" :subregion-3 "AOUZOU" :uuid "location1-uuid"}
1922
{:category "CONTINENT" :type "AFRICA" :uuid "location2-uuid"}
@@ -236,3 +239,72 @@
236239
"Checking invalid measurement names with quantities. Getting back the error map."
237240
'({:context-medium "atmosphere-at_cloud_base" :object "air_bad" :quantity "pressure"})
238241
{:MeasurementContextMedium "atmosphere-at_cloud_base", :MeasurementContextMediumURI nil, :MeasurementObject "air_bad", :MeasurementObjectURI nil, :MeasurementQuantities [{:Value "pressure", :MeasurementQuantityURI nil}]}))
242+
243+
(deftest blank-and-not-provided-long-name-validation-test
244+
(testing "Platform long name validation with blank and 'Not provided' values"
245+
(are3
246+
[long-name expected-uuid]
247+
(is (= expected-uuid
248+
(:uuid (kms-lookup/lookup-by-umm-c-keyword create-context :platforms
249+
{:short-name "PLAT2" :long-name long-name :type "Spacecraft"}))))
250+
251+
"Nil long name should match platform without long name in KMS"
252+
nil "plat2-uuid"
253+
254+
"Empty string long name should match platform without long name in KMS"
255+
"" "plat2-uuid"
256+
257+
"Case insensitive 'Not provided' should match"
258+
"nOt PrOvIdEd" "plat2-uuid"
259+
260+
"Invalid long name should not match even with valid short name"
261+
"Invalid Long Name" nil))
262+
263+
(testing "Instrument long name validation with blank and 'Not provided' values"
264+
(are3
265+
[long-name expected-uuid]
266+
(is (= expected-uuid
267+
(:uuid (kms-lookup/lookup-by-umm-c-keyword create-context :instruments
268+
{:short-name "INST2" :long-name long-name}))))
269+
270+
"Nil long name should match instrument without long name in KMS"
271+
nil "inst2-uuid"
272+
273+
"Empty string long name should match instrument without long name in KMS"
274+
"" "inst2-uuid"
275+
276+
"Case insensitive 'Not provided' should match"
277+
"nOt PrOvIdEd" "inst2-uuid"
278+
279+
"Invalid long name should not match even with valid short name"
280+
"Invalid Long Name" nil))
281+
282+
(testing "Project long name validation with blank and 'Not provided' values"
283+
(are3
284+
[long-name expected-uuid]
285+
(is (= expected-uuid
286+
(:uuid (kms-lookup/lookup-by-umm-c-keyword create-context :projects
287+
{:short-name "PROJ2" :long-name long-name}))))
288+
289+
"Nil long name should match project without long name in KMS"
290+
nil "proj2-uuid"
291+
292+
"Empty string long name should match project without long name in KMS"
293+
"" "proj2-uuid"
294+
295+
"Case insensitive 'Not provided' should match"
296+
"nOt PrOvIdEd" "proj2-uuid"
297+
298+
"Invalid long name should not match even with valid short name"
299+
"Invalid Long Name" nil))
300+
301+
(testing "Platforms with long names in KMS can be matched with or without long name"
302+
(is (= "plat1-uuid"
303+
(:uuid (kms-lookup/lookup-by-umm-c-keyword create-context :platforms
304+
{:short-name "PLAT1" :long-name nil :type "Aircraft"})))
305+
"Platform lookup with nil long name when KMS has long name should match (CMR-4400 behavior)")
306+
307+
(is (= "plat1-uuid"
308+
(:uuid (kms-lookup/lookup-by-umm-c-keyword create-context :platforms
309+
{:short-name "PLAT1" :long-name "Platform 1" :type "Aircraft"})))
310+
"Platform lookup with exact long name match should also match")))

system-int-test/test/cmr/system_int_test/ingest/collection/collection_keyword_validation_test.clj

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,60 @@
474474
"Case Insensitive"
475475
"Atm" "aIRBORNE Topographic Mapper"))
476476

477+
(testing "Blank and 'Not provided' long names should not cause validation errors"
478+
(testing "Platform with 'Not provided' long name should be valid"
479+
(assert-valid-keywords
480+
{:Platforms [(data-umm-cmn/platform {:ShortName "CESSNA 188"
481+
:LongName "Not provided"
482+
:Type "Propeller"})]}))
483+
484+
(testing "Instrument with 'Not provided' long name should be valid"
485+
(assert-valid-keywords
486+
{:Platforms
487+
[(data-umm-cmn/platform
488+
{:ShortName "A340-600"
489+
:LongName "Airbus A340-600"
490+
:Type "Jet"
491+
:Instruments [(data-umm-cmn/instrument {:ShortName "ACOUSTIC SOUNDERS"
492+
:LongName "Not provided"})]})]}))
493+
494+
(testing "Project with 'Not provided' long name should be valid"
495+
(assert-valid-keywords
496+
{:Projects [(assoc (data-umm-cmn/project "EUCREX-94" "") :LongName "Not provided")]}))
497+
498+
(testing "Invalid long names with valid short names should still fail validation"
499+
(testing "Platform with invalid long name should fail"
500+
(assert-invalid-keywords
501+
{:Platforms [(data-umm-cmn/platform {:ShortName "CESSNA 188"
502+
:LongName "Invalid Long Name"
503+
:Type "Propeller"})]}
504+
["Platforms" 0]
505+
[(format (str "Platform short name [%s], long name [%s], and type [%s]"
506+
" was not a valid keyword combination.")
507+
"CESSNA 188" "Invalid Long Name" "Propeller")]))
508+
509+
(testing "Instrument with invalid long name should fail"
510+
(assert-invalid-keywords
511+
{:Platforms
512+
[(data-umm-cmn/platform
513+
{:ShortName "A340-600"
514+
:LongName "Airbus A340-600"
515+
:Type "Jet"
516+
:Instruments [(data-umm-cmn/instrument {:ShortName "ACOUSTIC SOUNDERS"
517+
:LongName "Invalid Long Name"})]})]}
518+
["Platforms" 0 "Instruments" 0]
519+
[(format (str "Instrument short name [%s] and long name [%s]"
520+
" was not a valid keyword combination.")
521+
"ACOUSTIC SOUNDERS" "Invalid Long Name")]))
522+
523+
(testing "Project with invalid long name should fail"
524+
(assert-invalid-keywords
525+
{:Projects [(assoc (data-umm-cmn/project "EUCREX-94" "") :LongName "Invalid Long Name")]}
526+
["Projects" 0]
527+
[(format (str "Project short name [%s] and long name [%s]"
528+
" was not a valid keyword combination.")
529+
"EUCREX-94" "Invalid Long Name")]))))
530+
477531
(testing "Science Keyword validation"
478532
(are [attribs]
479533
(let [sk (data-umm-cmn/science-keyword attribs)]

0 commit comments

Comments
 (0)