Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions doc/release-notes/12439-openapi.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
## Updates for API Users

- The quality of the OpenAPI document has been greatly improved, increasing from 10/100 to 98/100 according [vacuum](https://quobix.com/vacuum/). See [the guides](https://dataverse-guide--12439.org.readthedocs.build/en/12439/api/getting-started.html#getting-the-openapi-document) and #12439.

## Updates for Developers

- If you are adding or updating Dataverse APIs you are now required to add OpenAPI annotations. See [the guides](https://dataverse-guide--12439.org.readthedocs.build/en/12439/developers/api-design.html#openapi) and #12439.
4 changes: 3 additions & 1 deletion doc/sphinx-guides/source/api/getting-started.rst
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,9 @@ The fully expanded example above (without environment variables) looks like this

curl "https://demo.dataverse.org/openapi?format=json"

We are aware that our OpenAPI document is not perfect. You can find more information about validating the document under :ref:`openapi-dev` in the Developer Guide.
If you have feedback about the our OpenAPI document, please contact us through the usual channels under :ref:`getting-help-with-apis`.

You can find more information about validating the document under :ref:`openapi-dev` in the Developer Guide.

Beyond "Getting Started" Tasks
------------------------------
Expand Down
32 changes: 29 additions & 3 deletions doc/sphinx-guides/source/developers/api-design.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,39 @@ OpenAPI

As you add API endpoints, please be conscious that we are exposing these endpoints as an OpenAPI document at ``/openapi`` (e.g. http://localhost:8080/openapi ). See :ref:`openapi` in the API Guide for the user-facing documentation.

We've played around with validation tools such as https://quobix.com/vacuum/ and https://pb33f.io/doctor/ only to discover that our OpenAPI output is less than ideal, generating various warnings and errors.
OpenAPI Annotations
~~~~~~~~~~~~~~~~~~~

To keep our OpenAPI document at a reasonable quality, you must add annotations to API endpoints you are adding and editing. Here's an example:

.. code-block:: java

@POST
@AuthRequired
@Path("/")
@Operation(summary = "Creates a license", description = "Creates a license when the authenticated user is a superuser and returns the created license location.")
@APIResponse(responseCode = "201", description = "License created with a Location header pointing to the new license.")
public Response addLicense(@Context ContainerRequestContext crc,
@RequestBody(description = "License definition to persist, including name, URI, active state, and sort order.",
content = @Content(mediaType = "application/json",
schema = @Schema(type = SchemaType.OBJECT,
description = "License definition accepted by the license administration API.")))
License license) {
...
}

If you are looking for a reference about the annotations used to generate the OpenAPI document, you can find it in the `MicroProfile OpenAPI Specification <https://download.eclipse.org/microprofile/microprofile-open-api-3.1/microprofile-openapi-spec-3.1.html#_detailed_usage_of_key_annotations>`_.

OpenAPI Quality
~~~~~~~~~~~~~~~

To use `vacuum <https://quobix.com/vacuum/>`_ with our recommended settings to check the quality of our OpenAPI document, see the :download:`README.md <../../../../scripts/openapi/README.md>` under ``scripts/openapi``.

You can prevent additional problems in our OpenAPI document by observing the following practices:

- When creating a method name within an API class, make it unique.

If you are looking for a reference about the annotations used to generate the OpenAPI document, you can find it in the `MicroProfile OpenAPI Specification <https://download.eclipse.org/microprofile/microprofile-open-api-3.1/microprofile-openapi-spec-3.1.html#_detailed_usage_of_key_annotations>`_.
- Check that HTTP GET and DELETE operations do not accept request bodies.
- Various notes under :download:`vacuum-recommended.yaml <../../../../scripts/openapi/vacuum-recommended.yaml>`.

Paths
-----
Expand Down
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1128,6 +1128,7 @@
<operationIdStrategy>CLASS_METHOD</operationIdStrategy>
<scanPackages>edu.harvard.iq.dataverse</scanPackages>
<scanDependenciesDisable>true</scanDependenciesDisable>
<filter>edu.harvard.iq.dataverse.openapi.DataverseOpenApiFilter</filter>
</configuration>
</execution>
</executions>
Comment thread
pdurbin marked this conversation as resolved.
Expand Down
71 changes: 71 additions & 0 deletions scripts/openapi/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# OpenAPI Quality Checks

This directory contains the Dataverse vacuum ruleset used to check the generated
OpenAPI document while preserving known legacy API shapes.

## vacuum

vacuum is a command-line linter and quality checker for OpenAPI, AsyncAPI, and
JSON Schema documents. It is compatible with Spectral rulesets, so the Dataverse
ruleset can extend the built-in `vacuum:oas` recommended profile and disable
rules that are noisy for the current API surface.

Project and documentation:

- GitHub: https://github.com/daveshanley/vacuum
- Docs: https://quobix.com/vacuum

Install with Homebrew:

```bash
brew install --cask daveshanley/vacuum/vacuum
```

Install with npm:

```bash
npm i -g @quobix/vacuum
```

Install with curl:

```bash
curl -fsSL https://quobix.com/scripts/install_vacuum.sh | sh
```

## Checking Dataverse OpenAPI

Generate the OpenAPI document from the repository root:

```bash
mvn -q -DskipTests process-classes
```

The generated JSON is written to:

```text
target/classes/META-INF/openapi.json
```

Run the vacuum checks from the repository root:

```bash
vacuum lint -r scripts/openapi/vacuum-recommended.yaml target/classes/META-INF/openapi.json
```

For a fuller report:

```bash
vacuum report -r scripts/openapi/vacuum-recommended.yaml target/classes/META-INF/openapi.json
```

For the interactive dashboard:

```bash
vacuum dashboard -r scripts/openapi/vacuum-recommended.yaml target/classes/META-INF/openapi.json
```

The ruleset extends `vacuum:oas` recommended rules, with legacy/noisy checks
disabled where Dataverse intentionally keeps historical endpoint names or lacks
examples. Request-body findings remain enabled so unused body parameters on GET
endpoints are reported and can be removed from source.
19 changes: 19 additions & 0 deletions scripts/openapi/vacuum-recommended.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
description: Dataverse OpenAPI recommended rules with legacy/noisy rules disabled.
extends: [[vacuum:oas, recommended]]
rules:
# Endpoint paths are historically camel case, and we won't change those for OpenAPI conformance
paths-kebab-case: false
# We do have duplications, because the same parameters as `id` will be used in multiple endpoints
description-duplication: false
# We don't have examples
oas3-missing-example: false
# We have /pids/{id}/delete --> see edu.harvard.iq.dataverse.api.Pids#deletePid
# The HTTP verb there is DELETE so actually we don't need to have the verb in the path,
# could be fixed.
no-http-verbs-in-path: false
# These endpoints have an unused `body` parameter, probably because of copy-paste.
# no-request-body: false would not report them, but rather we should fix them to remove `body`
# GET /files/{id}/prov-freeform
# GET /files/{id}/prov-json
# GET /files/{id}/prov-json
#no-request-body: false
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.util.Date;

import jakarta.persistence.*;
import org.eclipse.microprofile.openapi.annotations.media.Schema;

/**
*
Expand All @@ -16,6 +17,7 @@
}
)
@Entity
@Schema(description = "Alternate persistent identifier assigned to a Dataverse object, including protocol, authority, identifier, and registration state.")
public class AlternativePersistentIdentifier implements Serializable {

@Id
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/edu/harvard/iq/dataverse/AuxiliaryFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import jakarta.persistence.NamedNativeQuery;
import jakarta.persistence.NamedQueries;
import jakarta.persistence.NamedQuery;
import org.eclipse.microprofile.openapi.annotations.media.Schema;

/**
*
Expand All @@ -38,6 +39,7 @@
query = "select distinct type from auxiliaryfile where datafile_id = ?1")
})
@Entity
@Schema(description = "Metadata for an auxiliary file associated with a data file, including format, origin, visibility, size, checksum, and type.")
public class AuxiliaryFile implements Serializable {

@Id
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/edu/harvard/iq/dataverse/BannerMessage.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import org.eclipse.microprofile.openapi.annotations.media.Schema;


/**
*
* @author skraffmi
*/
@Entity
@Schema(description = "Site banner message configuration, including active state, dismissibility, and localized message text.")
public class BannerMessage implements Serializable {

@Id
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/edu/harvard/iq/dataverse/BannerMessageText.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import org.eclipse.microprofile.openapi.annotations.media.Schema;

/**
*
* @author skraffmi
*/
@Entity
@Schema(description = "Localized banner message text, including the language code and message content.")
public class BannerMessageText implements Serializable {

@Id
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/edu/harvard/iq/dataverse/DatasetLock.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import jakarta.persistence.TemporalType;
import jakarta.persistence.NamedQueries;
import jakarta.persistence.NamedQuery;
import org.eclipse.microprofile.openapi.annotations.media.Schema;

/**
*
Expand All @@ -64,6 +65,7 @@
)
public class DatasetLock implements Serializable {

@Schema(description = "Dataset lock reason indicating why dataset editing or publication is blocked.")
public enum Reason {
/** Data being ingested *//** Data being ingested */
Ingest,
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/edu/harvard/iq/dataverse/DvObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import java.util.logging.Logger;

import jakarta.persistence.*;
import org.eclipse.microprofile.openapi.annotations.media.Schema;

/**
* Base of the object hierarchy for "anything that can be inside a dataverse".
Expand Down Expand Up @@ -58,6 +59,7 @@ public abstract class DvObject extends DataverseEntity implements java.io.Serial

private static final Logger logger = Logger.getLogger(DvObject.class.getCanonicalName());

@Schema(description = "Dataverse object type discriminator for dataverses, datasets, and data files.")
public enum DType {
Dataverse("Dataverse"), Dataset("Dataset"),DataFile("DataFile");

Expand Down
Loading
Loading