Skip to content

Commit

Permalink
Merge pull request #10331 from Recherche-Data-Gouv/10316_cvoc_http_he…
Browse files Browse the repository at this point in the history
…aders

CVOC : add HTTP request headers (Ontoportal integration)
  • Loading branch information
pdurbin committed Apr 17, 2024
2 parents 4678330 + 776fb38 commit e6b2661
Show file tree
Hide file tree
Showing 9 changed files with 34 additions and 6 deletions.
5 changes: 5 additions & 0 deletions doc/release-notes/10316_cvoc_http_headers.md
@@ -0,0 +1,5 @@
You are now able to add HTTP request headers required by the External Vocabulary Services you are implementing.

A combined documentation can be found on pull request [#10404](https://github.com/IQSS/dataverse/pull/10404).

For more information, see issue [#10316](https://github.com/IQSS/dataverse/issues/10316) and pull request [gddc/dataverse-external-vocab-support#19](https://github.com/gdcc/dataverse-external-vocab-support/pull/19).
6 changes: 4 additions & 2 deletions doc/sphinx-guides/source/admin/metadatacustomization.rst
Expand Up @@ -552,6 +552,8 @@ Great care must be taken when reloading a metadata block. Matching is done on fi

The ability to reload metadata blocks means that SQL update scripts don't need to be written for these changes. See also the :doc:`/developers/sql-upgrade-scripts` section of the Developer Guide.

.. _using-external-vocabulary-services:

Using External Vocabulary Services
----------------------------------

Expand All @@ -577,9 +579,9 @@ In general, the external vocabulary support mechanism may be a better choice for
The specifics of the user interface for entering/selecting a vocabulary term and how that term is then displayed are managed by third-party Javascripts. The initial Javascripts that have been created provide auto-completion, displaying a list of choices that match what the user has typed so far, but other interfaces, such as displaying a tree of options for a hierarchical vocabulary, are possible.
Similarly, existing scripts do relatively simple things for displaying a term - showing the term's name in the appropriate language and providing a link to an external URL with more information, but more sophisticated displays are possible.

Scripts supporting use of vocabularies from services supporting the SKOMOS protocol (see https://skosmos.org) and retrieving ORCIDs (from https://orcid.org) are available https://github.com/gdcc/dataverse-external-vocab-support. (Custom scripts can also be used and community members are encouraged to share new scripts through the dataverse-external-vocab-support repository.)
Scripts supporting use of vocabularies from services supporting the SKOMOS protocol (see https://skosmos.org), retrieving ORCIDs (from https://orcid.org), and using ROR (https://ror.org/) are available https://github.com/gdcc/dataverse-external-vocab-support. (Custom scripts can also be used and community members are encouraged to share new scripts through the dataverse-external-vocab-support repository.)

Configuration involves specifying which fields are to be mapped, whether free-text entries are allowed, which vocabulary(ies) should be used, what languages those vocabulary(ies) are available in, and several service protocol and service instance specific parameters.
Configuration involves specifying which fields are to be mapped, whether free-text entries are allowed, which vocabulary(ies) should be used, what languages those vocabulary(ies) are available in, and several service protocol and service instance specific parameters, including the ability to send HTTP headers on calls to the service.
These are all defined in the :ref:`:CVocConf <:CVocConf>` setting as a JSON array. Details about the required elements as well as example JSON arrays are available at https://github.com/gdcc/dataverse-external-vocab-support, along with an example metadata block that can be used for testing.
The scripts required can be hosted locally or retrieved dynamically from https://gdcc.github.io/ (similar to how dataverse-previewers work).

Expand Down
8 changes: 6 additions & 2 deletions doc/sphinx-guides/source/installation/config.rst
Expand Up @@ -4478,9 +4478,13 @@ A boolean setting that, if true, will send an email and notification to users wh
:CVocConf
+++++++++

A JSON-structured setting that configures Dataverse to associate specific metadatablock fields with external vocabulary services and specific vocabularies/sub-vocabularies managed by that service. More information about this capability is available at :doc:`/admin/metadatacustomization`.
The ``:CVocConf`` database setting is used to allow metadatablock fields to look up values in external vocabulary services. For example, you could configure the "Author Affiliation" field to look up organizations in the `Research Organization Registry (ROR) <https://ror.org>`_. For a high-level description of this feature, see :ref:`using-external-vocabulary-services` in the Admin Guide.

Scripts that implement this association for specific service protocols are maintained at https://github.com/gdcc/dataverse-external-vocab-support. That repository also includes a json-schema for validating the structure required by this setting along with an example metadatablock and sample :CVocConf setting values associating entries in the example block with ORCID and SKOSMOS based services.
The expected format for the ``:CVocConf`` database setting is JSON but the details are not documented here. Instead, please refer to `docs/readme.md <https://github.com/gdcc/dataverse-external-vocab-support/blob/main/docs/readme.md>`_ in the https://github.com/gdcc/dataverse-external-vocab-support repo.

That repository also includes scripts that implement the lookup for specific service protocols, a JSON Schema for validating the structure required by this setting, and an example metadatablock with a sample ``:CVocConf`` config that associates fields in the example block with ORCID and SKOSMOS based services.

The commands below should give you an idea of how to load the configuration, but you'll want to study the examples and make decisions about which configuration to use:

``wget https://gdcc.github.io/dataverse-external-vocab-support/examples/config/cvoc-conf.json``

Expand Down
Expand Up @@ -505,7 +505,14 @@ public void process(HttpResponse response, HttpContext context) throws HttpExcep
HttpGet httpGet = new HttpGet(retrievalUri);
//application/json+ld is for backward compatibility
httpGet.addHeader("Accept", "application/ld+json, application/json+ld, application/json");

//Adding others custom HTTP request headers if exists
final JsonObject headers = cvocEntry.getJsonObject("headers");
if (headers != null) {
final Set<String> headerKeys = headers.keySet();
for (final String hKey: headerKeys) {
httpGet.addHeader(hKey, headers.getString(hKey));
}
}
HttpResponse response = httpClient.execute(httpGet);
String data = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
int statusCode = response.getStatusLine().getStatusCode();
Expand Down
1 change: 1 addition & 0 deletions src/main/webapp/datasetFieldForEditFragment.xhtml
Expand Up @@ -42,6 +42,7 @@
</c:if>
<f:passThroughAttribute name="lang" value="#{DatasetPage.getFieldLanguage(cvoc.getString('languages'))}"/>
<f:passThroughAttribute name="data-cvoc-service-url" value="#{cvoc.getString('cvoc-url','')}"/>
<f:passThroughAttribute name="data-cvoc-headers" value="#{cvoc.containsKey('headers') ? cvoc.get('headers').toString() : '{}'}"/>
<f:passThroughAttribute name="data-cvoc-protocol" value="#{cvoc.getString('protocol','')}"/>
<f:passThroughAttribute name="data-cvoc-filter" value="#{cvoc.getString('term-parent-uri','')}"/>
<f:passThroughAttribute name="data-cvoc-vocabs" value="#{cvoc.get('vocabs').toString()}"/>
Expand Down
4 changes: 4 additions & 0 deletions src/main/webapp/metadataFragment.xhtml
Expand Up @@ -88,6 +88,7 @@
escape="#{dsf.datasetFieldType.isEscapeOutputText()}">
<f:passThroughAttribute name="lang" value="#{DatasetPage.getFieldLanguage(cvocConf.get(dsf.datasetFieldType.id).getString('languages'))}" />
<f:passThroughAttribute name="data-cvoc-service-url" value="#{cvocConf.get(dsf.datasetFieldType.id).getString('cvoc-url','')}" />
<f:passThroughAttribute name="data-cvoc-headers" value="#{cvocConf.get(dsf.datasetFieldType.id).containsKey('headers') ? cvocConf.get(dsf.datasetFieldType.id).get('headers').toString() : '{}'}"/>
<f:passThroughAttribute name="data-cvoc-protocol" value="#{cvocConf.get(dsf.datasetFieldType.id).getString('protocol','')}" />
<f:passThroughAttribute name="data-cvoc-managedfields" value="#{cvocConf.get(dsf.datasetFieldType.id).get('managedfields').toString()}" />
</h:outputText>
Expand All @@ -100,6 +101,7 @@
escape="#{dsf.datasetFieldType.isEscapeOutputText()}">
<f:passThroughAttribute name="lang" value="#{DatasetPage.getFieldLanguage(cvocConf.get(dsf.datasetFieldType.id).getString('languages'))}" />
<f:passThroughAttribute name="data-cvoc-service-url" value="#{cvocConf.get(dsf.datasetFieldType.id).getString('cvoc-url','')}" />
<f:passThroughAttribute name="data-cvoc-headers" value="#{cvocConf.get(dsf.datasetFieldType.id).containsKey('headers') ? cvocConf.get(dsf.datasetFieldType.id).get('headers').toString() : '{}'}"/>
<f:passThroughAttribute name="data-cvoc-protocol" value="#{cvocConf.get(dsf.datasetFieldType.id).getString('protocol','')}" />
<f:passThroughAttribute name="data-cvoc-managedfields" value="#{cvocConf.get(dsf.datasetFieldType.id).get('managedfields').toString()}" />
</h:outputText>
Expand Down Expand Up @@ -146,6 +148,7 @@
rendered="${cvocOnCvPart and cvPart.key.datasetFieldType.name.equals(cvocConf.get(cvPart.key.datasetFieldType.id).getString('term-uri-field'))}">
<f:passThroughAttribute name="lang" value="#{DatasetPage.getFieldLanguage(cvocConf.get(cvPart.key.datasetFieldType.id).getString('languages'))}" />
<f:passThroughAttribute name="data-cvoc-service-url" value="#{cvocConf.get(cvPart.key.datasetFieldType.id).getString('cvoc-url','')}" />
<f:passThroughAttribute name="data-cvoc-headers" value="#{cvocConf.get(cvPart.key.datasetFieldType.id).containsKey('headers') ? cvocConf.get(cvPart.key.datasetFieldType.id).get('headers').toString() : '{}'}"/>
<f:passThroughAttribute name="data-cvoc-protocol" value="#{cvocConf.get(cvPart.key.datasetFieldType.id).getString('protocol','')}" />
<!-- unlikely to be used in this case -->
<f:passThroughAttribute name="data-cvoc-managedfields" value="#{cvocConf.get(cvPart.key.datasetFieldType.id).get('managedfields').toString()}" />
Expand All @@ -156,6 +159,7 @@
rendered="${cvocOnDsf and cvPart.key.datasetFieldType.name.equals(cvocConf.get(dsf.datasetFieldType.id).getString('term-uri-field'))}">
<f:passThroughAttribute name="lang" value="#{DatasetPage.getFieldLanguage(cvocConf.get(dsf.datasetFieldType.id).getString('languages'))}" />
<f:passThroughAttribute name="data-cvoc-service-url" value="#{cvocConf.get(dsf.datasetFieldType.id).getString('cvoc-url','')}" />
<f:passThroughAttribute name="data-cvoc-headers" value="#{cvocConf.get(dsf.datasetFieldType.id).containsKey('headers') ? cvocConf.get(dsf.datasetFieldType.id).get('headers').toString() : '{}'}"/>
<f:passThroughAttribute name="data-cvoc-protocol" value="#{cvocConf.get(dsf.datasetFieldType.id).getString('protocol','')}" />
<f:passThroughAttribute name="data-cvoc-managedfields" value="#{cvocConf.get(dsf.datasetFieldType.id).get('managedfields').toString()}" />
</h:outputText>
Expand Down
4 changes: 4 additions & 0 deletions src/main/webapp/search-include-fragment.xhtml
Expand Up @@ -213,6 +213,7 @@
<c:if test="#{!cvocConf.isEmpty()}">
<f:passThroughAttribute name="lang" value="#{DatasetPage.getFieldLanguage(cvocConf.get(facetCategory.datasetFieldTypeId).getString('languages'))}" />
<f:passThroughAttribute name="data-cvoc-service-url" value="#{cvocConf.get(facetCategory.datasetFieldTypeId).getString('cvoc-url','')}" />
<f:passThroughAttribute name="data-cvoc-headers" value="#{cvocConf.get(facetCategory.datasetFieldTypeId).containsKey('headers') ? cvocConf.get(facetCategory.datasetFieldTypeId).get('headers').toString() : '{}'}"/>
<f:passThroughAttribute name="data-cvoc-protocol" value="#{cvocConf.get(facetCategory.datasetFieldTypeId).getString('protocol','')}" />
</c:if>
</h:outputText>
Expand All @@ -234,6 +235,7 @@
<c:if test="#{!cvocConf.isEmpty()}">
<f:passThroughAttribute name="lang" value="#{DatasetPage.getFieldLanguage(cvocConf.get(facetCategory.datasetFieldTypeId).getString('languages'))}" />
<f:passThroughAttribute name="data-cvoc-service-url" value="#{cvocConf.get(facetCategory.datasetFieldTypeId).getString('cvoc-url','')}" />
<f:passThroughAttribute name="data-cvoc-headers" value="#{cvocConf.get(facetCategory.datasetFieldTypeId).containsKey('headers') ? cvocConf.get(facetCategory.datasetFieldTypeId).get('headers').toString() : '{}'}"/>
<f:passThroughAttribute name="data-cvoc-protocol" value="#{cvocConf.get(facetCategory.datasetFieldTypeId).getString('protocol','')}" />
</c:if>
</h:outputText>
Expand Down Expand Up @@ -384,6 +386,7 @@
<h:outputText value="#{friendlyNames.get(1)}">
<f:passThroughAttribute name="lang" value="#{DatasetPage.getFieldLanguage(cvocConf.get(SearchIncludeFragment.getFieldTypeId(friendlyNames.get(0))).getString('languages'))}" />
<f:passThroughAttribute name="data-cvoc-service-url" value="#{cvocConf.get(SearchIncludeFragment.getFieldTypeId(friendlyNames.get(0))).getString('cvoc-url','')}" />
<f:passThroughAttribute name="data-cvoc-headers" value="#{cvocConf.get(SearchIncludeFragment.getFieldTypeId(friendlyNames.get(0))).containsKey('headers') ? cvocConf.get(SearchIncludeFragment.getFieldTypeId(friendlyNames.get(0))).get('headers').toString() : '{}'}"/>
<f:passThroughAttribute name="data-cvoc-protocol" value="#{cvocConf.get(SearchIncludeFragment.getFieldTypeId(friendlyNames.get(0))).getString('protocol','')}" />
</h:outputText>
<span class="glyphicon glyphicon-remove"></span>
Expand Down Expand Up @@ -621,6 +624,7 @@
<c:if test="#{!cvocConf.isEmpty()}">
<f:passThroughAttribute name="lang" value="#{DatasetPage.getFieldLanguage(cvocConf.get(datasetFieldServiceBean.findByName(highlight.solrField.nameSearchable).id).getString('languages'))}" />
<f:passThroughAttribute name="data-cvoc-service-url" value="#{cvocConf.get(datasetFieldServiceBean.findByName(highlight.solrField.nameSearchable).id).getString('cvoc-url','')}" />
<f:passThroughAttribute name="data-cvoc-headers" value="#{cvocConf.get(datasetFieldServiceBean.findByName(highlight.solrField.nameSearchable).id).containsKey('headers') ? cvocConf.get(datasetFieldServiceBean.findByName(highlight.solrField.nameSearchable).id).get('headers').toString() : '{}'}"/>
<f:passThroughAttribute name="data-cvoc-protocol" value="#{cvocConf.get(datasetFieldServiceBean.findByName(highlight.solrField.nameSearchable).id).getString('protocol','')}" />
</c:if>
</h:outputText>
Expand Down
1 change: 1 addition & 0 deletions src/main/webapp/search/advanced.xhtml
Expand Up @@ -121,6 +121,7 @@
<p:inputText id="searchValue2" styleClass="form-control" value="#{item.searchValue}" rendered="#{empty item.controlledVocabularyValues}">
<f:passThroughAttribute name="lang" value="#{AdvancedSearchPage.getFieldLanguage(cvoc.getString('languages'))}"/>
<f:passThroughAttribute name="data-cvoc-service-url" value="#{cvoc.getString('cvoc-url','')}"/>
<f:passThroughAttribute name="data-cvoc-headers" value="#{cvoc.containsKey('headers') ? cvoc.get('headers').toString() : '{}'}"/>
<f:passThroughAttribute name="data-cvoc-protocol" value="#{cvoc.getString('protocol','')}"/>
<f:passThroughAttribute name="data-cvoc-filter" value="#{cvoc.getString('term-parent-uri','')}"/>
<f:passThroughAttribute name="data-cvoc-vocabs" value="#{cvoc.get('vocabs').toString()}"/>
Expand Down
2 changes: 1 addition & 1 deletion src/test/java/edu/harvard/iq/dataverse/api/SearchIT.java
Expand Up @@ -862,7 +862,7 @@ public void testNestedSubtree() {
Response publishDataset = UtilIT.publishDatasetViaNativeApi(datasetPid, "major", apiToken);
publishDataset.then().assertThat()
.statusCode(OK.getStatusCode());

UtilIT.sleepForReindex(datasetPid, apiToken, 5);
Response searchPublishedSubtreeWDS = UtilIT.search(searchPart, apiToken, "&subtree="+dataverseAlias);
searchPublishedSubtreeWDS.prettyPrint();
searchPublishedSubtreeWDS.then().assertThat()
Expand Down

0 comments on commit e6b2661

Please sign in to comment.