diff --git a/ingest-app/docs/api.md b/ingest-app/docs/api.md
index 5d263977de..3d9b6eb699 100644
--- a/ingest-app/docs/api.md
+++ b/ingest-app/docs/api.md
@@ -1493,9 +1493,9 @@ Supported metadata formats:
* OPeNDAP url in RelatedUrls for UMM-G format
* OPeNDAP url in OnlineResources for ECHO10 format
-Input for this update type should be a list of granule URs. UMM-G Granules listed will have any `RelatedUrl`s containg the string `"opendap"` updated to include `"Type": "USE SERVICE API"` and `"Subtype": "OPENDAP DATA"`.
+Input for this update type should be a list of granule URs. UMM-G Granules listed will have any `RelatedUrl`s with a *URL* containing the string `"opendap"` updated to include `"Type": "USE SERVICE API"` and `"Subtype": "OPENDAP DATA"`. Echo10 Granules will have any `OnlineResources` with a *type* containing `"opendap"` updated to to include `USE SERVICE API : OPENDAP DATA`.
-As an alternative to identifying links via the `"opendap"` string method, a subtype string can be supplied with each granule UR as a tuple. If supplied, any links with a subtype matching this input string will be updated instead. As before, the link will be updated to include `"Type": "USE SERVICE API"` and `"Subtype": "OPENDAP DATA"`.
+As an alternative to identifying links via the `"opendap"` string method, a type string can be supplied with each granule UR as a tuple. If supplied, any UMM-G links with a *subtype* matching this input string will be updated instead. As before, the link will be updated to include `"Type": "USE SERVICE API"` and `"Subtype": "OPENDAP DATA"`. Any Echo10 links with a *type* matching this input will have their major typing updated to `USE SERVICE API`.
Examples for each update format are provided below. For the first update, each granule in the list will have any links containing the string `"opendap"` updated to the new Type and Subtype.
diff --git a/ingest-app/src/cmr/ingest/services/granule_bulk_update/opendap/echo10.clj b/ingest-app/src/cmr/ingest/services/granule_bulk_update/opendap/echo10.clj
index 8425a62ab0..8a8e1cd30c 100644
--- a/ingest-app/src/cmr/ingest/services/granule_bulk_update/opendap/echo10.clj
+++ b/ingest-app/src/cmr/ingest/services/granule_bulk_update/opendap/echo10.clj
@@ -4,6 +4,7 @@
[clojure.data.xml :as xml]
[clojure.string :as string]
[clojure.zip :as zip]
+ [cmr.common.services.errors :as errors]
[cmr.common.util :refer [remove-nil-keys]]
[cmr.common.xml :as cx]
[cmr.ingest.services.granule-bulk-update.opendap.opendap-util
@@ -74,10 +75,12 @@
"Returns the updated OPeNDAP type (cloud or on-prem) resource based on the opendap resources,
the opendap type and the url-map that is parsed from the update url value."
[opendap-type opendap-resources url-map]
- (->> opendap-resources
- (filter #(= opendap-type (opendap-util/url->opendap-type (:url %))))
- first
- (update-opendap-resource (first (opendap-type url-map)))))
+ (let [resources (filter #(= opendap-type (opendap-util/url->opendap-type (:url %))) opendap-resources)]
+ (when (< 1 (count resources))
+ (errors/throw-service-errors :invalid-data
+ [(str "Cannot update granule - more than one Hyrax-in-the-cloud or"
+ " more than one on-prem OPeNDAP link was detected in the granule")]))
+ (update-opendap-resource (first (opendap-type url-map)) (first resources))))
(defn- updated-online-resources
"Take the parsed online resources in the original metadata, update the existing opendap url
diff --git a/system-int-test/resources/CMR-7503-echo10-bad-data.xml b/system-int-test/resources/CMR-7503-echo10-bad-data.xml
new file mode 100644
index 0000000000..5bb86b6a53
--- /dev/null
+++ b/system-int-test/resources/CMR-7503-echo10-bad-data.xml
@@ -0,0 +1,62 @@
+
+ granule_with_duplicate_opendap_types
+ 2015-08-19T19:31:03.105Z
+ 2019-12-06T16:40:19.803Z
+
+ MUR-JPL-L4-GLOB-v4.1
+ 4.1
+
+
+ 331.33482456207275
+ UNSPECIFIED
+ 2021-08-19T10:35:04.000Z
+
+
+
+ 2021-08-02T09:00:00.000Z
+ 2021-08-02T09:00:00.000Z
+
+
+
+
+
+
+ -179.641
+ 53.855
+ 58.885
+ -87.300
+
+
+
+
+
+
+
+
+
+ https://podaac-tools.jpl.nasa.gov/drive/files/allData/ghrsst/data/GDS2/L4/GLOB/JPL/MUR/v4.1/2002/153/20020602090000-JPL-L4_GHRSST-SSTfnd-MUR-GLOB-v02.0-fv04.1.nc
+ The HTTP location for the granule.
+
+
+
+
+ https://podaac-opendap.jpl.nasa.gov/opendap/allData/ghrsst/data/GDS2/L4/GLOB/JPL/MUR/v4.1/2002/153/20020602090000-JPL-L4_GHRSST-SSTfnd-MUR-GLOB-v02.0-fv04.1.nc.html
+ The OPENDAP location for the granule.
+ OPENDAP
+ text/html
+
+
+ https://otherResource.foo
+ The OTHER location for the granule.
+ OTHER LINK
+ text/html
+
+
+ https://link-1
+ a generic link with the wrong mime-type
+ OPENDAP DATA
+ text/error+csv
+
+
+ NETCDF
+
diff --git a/system-int-test/test/cmr/system_int_test/ingest/granule_bulk_update/granule_bulk_update_test.clj b/system-int-test/test/cmr/system_int_test/ingest/granule_bulk_update/granule_bulk_update_test.clj
index 4766cd8887..b926d4ef00 100644
--- a/system-int-test/test/cmr/system_int_test/ingest/granule_bulk_update/granule_bulk_update_test.clj
+++ b/system-int-test/test/cmr/system_int_test/ingest/granule_bulk_update/granule_bulk_update_test.clj
@@ -1624,7 +1624,13 @@
:concept-type :granule
:native-id "test-gran1"
:format-key :echo10})
- {:keys [concept-id revision-id]} granule]
+ {:keys [concept-id revision-id]} granule
+ bad-granule (data-core/ingest-concept-with-metadata-file
+ "CMR-7503-echo10-bad-data.xml"
+ {:provider-id "PROV1"
+ :concept-type :granule
+ :native-id "test-gran2"
+ :format-key :echo10})]
(index/wait-until-indexed)
@@ -1657,4 +1663,27 @@
(let [latest-metadata (:metadata (mdb/get-concept concept-id))]
(is (= (slurp (io/resource "CMR-7503-echo10-online-resource-type_updated_by_granuleur_and_type.xml"))
- latest-metadata))))))))
+ latest-metadata)))))
+
+ (testing "error due to duplicate link types in granule"
+ (let [bulk-update {:name "update opendap link online resource type 3"
+ :operation "UPDATE_TYPE"
+ :update-field "OPeNDAPLink"
+ :updates ["granule_with_duplicate_opendap_types"]}
+ {:keys [status task-id errors] :as response} (ingest/bulk-update-granules "PROV1" bulk-update bulk-update-options)]
+
+ (ingest/update-granule-bulk-update-task-statuses)
+ (index/wait-until-indexed)
+
+ (is (= 200 status))
+ (is (some? task-id))
+
+ (let [status-req-options {:query-params {:show_granules "true"}}
+ status-response (ingest/granule-bulk-update-task-status task-id status-req-options)
+ {:keys [task-status status-message granule-statuses]} status-response]
+ (is (= "COMPLETE" task-status))
+ (is (= [{:granule-ur "granule_with_duplicate_opendap_types"
+ :status "FAILED"
+ :status-message (str "Cannot update granule - more than one Hyrax-in-the-cloud "
+ "or more than one on-prem OPeNDAP link was detected in the granule")}]
+ granule-statuses))))))))