Cloud Software Group, Inc. EBX®
Documentation > Developer Guide
Navigation mode Documentation > Developer Guide

# REST Toolkit

## Introduction

TIBCO EBX® offers the possibility to develop custom REST services using the REST Toolkit. The REST Toolkit supports Jakarta RESTful Web Services 3.1 and JSR-367: JSON-B.

A REST service is implemented by a Java class and its operations are implemented by Java methods. The response can be generated by serializing POJO objects. The request input can be unserialized to POJOs. Various input and output formats, including JSON, are supported. For more details on supported formats, see media types.

Rest Toolkit supports the following:

- Injectable objects

  EBX® provides injectable objects useful to authenticate the request's user, to access the EBX® repository or to built URIs without worrying about the configuration (for example reverse-proxy or REST forward modes);

  JAX-RS injectable objects are also supported.

- Annotations

  EBX® provides annotations to describe resources, grant anonymous access or add restrictions to a method.

  JAX-RS ans JSON-B annotations are also supported.

- logging and debugging utilities.

##### See also

- Jakarta RESTful Web Services 3.1 API Specification

## Application definitions

An EBX® module, that includes custom REST services, must provide at least one REST Toolkit application class. A REST Toolkit application class extends the EBX® ``RESTApplicationAbstract`` class. The minimum requirement is to define the base URL, using the `@ApplicationPath` annotation and the set of packages to scan for REST service classes.

##### Note

Only packages accessible from the web application's classloader can be scanned.

##### Note

It is possible to register REST resource classes or singletons, packaged inside or outside the web application archive, through the ApplicationConfigurator.register(java.lang.Class) or ApplicationConfigurator.register(java.lang.Object) methods.

##### Note

If no packages scope is defined, then every class reachable from the web application's classloader will be scanned.

The application path cannot be "`/`" and must not collide with an existing resource from the module. It is recommended to use "`/rest`" (the value of the `RESTApplicationAbstract.REST_DEFAULT_APPLICATION_PATH` constant).

EBX® ``Documentation`` annotation is optional. It is displayed to administrators in 'Technical configuration' > 'Modules and data models' or when logging and debugging.

    import jakarta.ws.rs.*;

    import com.orchestranetworks.rest.*;
    import com.orchestranetworks.rest.annotation.*;

    @ApplicationPath(RESTApplicationAbstract.REST_DEFAULT_APPLICATION_PATH)
    @Documentation("My REST sample application")
    public final class RESTApplication extends RESTApplicationAbstract
    {
       public RESTApplication()
       {
          // Adds one or more package names which will be used to scan for components.
          super((cfg) -> cfg.addPackages(RESTApplication.class.getPackage()));
       }
    }

## Service and operation definitions

A REST Toolkit service is implemented by a Java class and its operations are implemented by its methods.

Class and methods can be annotated by `@Path` to specify the access path. The `@Path` annotation value defined at the class level will prepend the ones defined on methods. Warning, only one `@Path` annotation is allowed per class or method.

Media types accepted and produced by a resource are respectively defined by the `@Consumes` and `@Produces` JAX-RS annotations. The supported media types are:

- `application/json` ( MediaType.APPLICATION_JSON_TYPE )

- `application/octet-stream` ( MediaType.APPLICATION_OCTET_STREAM_TYPE )

- `application/x-www-form-urlencoded` ( MediaType.APPLICATION_FORM_URLENCODED_TYPE )

- `multipart/form-data` ( MediaType.MULTIPART_FORM_DATA_TYPE )

- `text/css`

- `text/html` ( MediaType.TEXT_HTML_TYPE )

- `text/plain` ( MediaType.TEXT_PLAIN_TYPE )

Valid HTTP(S) methods are specified by JAX-RS annotations `@GET`, `@POST`, `@PUT`, etc. Only one of these annotations can be set on each Java method (this means that a Java method can support only one HTTP method).

Warning: URL parameters with a name prefixed with `ebx-` are reserved by REST Toolkit and should not be defined by custom REST services, unless explicitly authorized by the EBX® documentation.

### URL and sample

The REST URL to access the description service for the sample is defined below:

> `http[s]://<host>[:<port>]/<path to webapp>/rest/track/v1/description`

Where:

- `<path to webapp>` corresponds to the web application's path holding the REST Toolkit application, itself serving the sample service. The path is composed by multiple, or none, URI segments followed by the web application's name.

##### Note

Please note that `/rest/track/v1/description` corresponds to the concatenation of the application's `@ApplicationPath` and service's `@Path` annotations.

The following REST Toolkit service sample provides methods to query and manage track data:

    import java.net.*;
    import java.util.*;
    import java.util.concurrent.*;
    import java.util.regex.*;
    import java.util.stream.*;

    import jakarta.servlet.http.*;
    import jakarta.ws.rs.*;
    import jakarta.ws.rs.container.*;
    import jakarta.ws.rs.core.*;

    import com.orchestranetworks.rest.annotation.*;
    import com.orchestranetworks.rest.inject.*;

    /**
     * The REST Toolkit Track service v1.
     */
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    @Path("/track/v1")
    @Documentation("Track service")
    public final class TrackService
    {
    	@Context
    	private ResourceInfo resourceInfo;

    	@Context
    	private SessionContext sessionContext;

    	private static final Map<Integer, TrackDTO> TRACKS = new ConcurrentHashMap<>();

    	/**
    	 * Gets service description
    	 */
    	@GET
    	@Path("/description")
    	@Documentation("Gets service description")
    	@Produces({ MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON })
    	@AnonymousAccessEnabled
    	public String handleServiceDescription()
    	{
    		return this.resourceInfo.getResourceMethod().getAnnotation(Documentation.class).value();
    	}

    	/**
    	 * Selects tracks.
    	 */
    	@GET
    	@Path("/tracks")
    	@Documentation("Selects tracks")
    	public Collection<TrackDTO> handleSelectTracks(
    			@QueryParam("singerFilter") final String singerFilter, // a URL parameter holding a Java regular expression
    			@QueryParam("titleFilter") final String titleFilter) // a URL parameter holding a Java regular expression
    	{
    		final Pattern singerPattern = TrackService.compilePattern(singerFilter);
    		final Pattern titlePattern = TrackService.compilePattern(titleFilter);

    		return TRACKS.values()
    				.stream()
    				.filter(Objects::nonNull)
    				.filter(track -> singerPattern == null || singerPattern.matcher(track.singer).matches())
    				.filter(track -> titlePattern == null || titlePattern.matcher(track.title).matches())
    				.collect(Collectors.toList());
    	}

    	private static Pattern compilePattern(final String aPattern)
    	{
    		if (aPattern == null || aPattern.isEmpty())
    			return null;

    		try
    		{
    			return Pattern.compile(aPattern);
    		}
    		catch (final PatternSyntaxException ignore)
    		{
    			// ignore invalid pattern
    			return null;
    		}
    	}

    	/**
    	 * Counts all tracks.
    	 */
    	@GET
    	@Path("/tracks:count")
    	@Documentation("Counts all tracks")
    	public int handleCountTracks()
    	{
    		return TRACKS.size();
    	}

    	/**
    	 * Selects a track by id.
    	 */
    	@GET
    	@Path("/tracks/{id}")
    	@Documentation("Selects a track by id")
    	public TrackDTO handleSelectTrackById(@PathParam("id") Integer id)
    	{
    		final TrackDTO track = TRACKS.get(id);
    		if (track == null)
    			throw new NotFoundException("Track id [" + id + "] does not found.");
    		return track;
    	}

    	/**
    	 * Deletes a track by id.
    	 */
    	@DELETE
    	@Path("/tracks/{id}")
    	@Documentation("Deletes a track by id")
    	public void handleDeleteTrackById(@PathParam("id") Integer id)
    	{
    		if (!TRACKS.containsKey(id))
    			throw new NotFoundException("Track id [" + id + "] does not found.");
    		TRACKS.remove(id);
    	}

    	/**
    	 * Inserts or updates one or several tracks.
    	 * <p>
    	 * The complex response structure corresponds to one of:
    	 * <ul>
    	 *  <li>An empty content with the <code>location<code> HTTP header defined
    	 *   to the access URI.</li>
    	 *  <li>A JSON array of {@link ResultDetailsDTO} objects.</li>
    	 * </ul>
    	 */
    	@POST
    	@Path("/tracks")
    	@Documentation("Inserts or updates one or several tracks")
    	public Response handleInsertOrUpdateTracks(List<TrackDTO> tracks)
    	{
    		int inserted = 0;
    		int updated = 0;

    		final ResultDetailsDTO[] resultDetails = new ResultDetailsDTO[tracks.size()];
    		int resultIndex = 0;

    		final URI base = this.sessionContext.getURIInfoUtility()
    				.createBuilderForRESTApplication()
    				.path(this.getClass())
    				.segment("tracks")
    				.build();

    		for (final TrackDTO track : tracks)
    		{
    			final String id = String.valueOf(track.id);
    			final URI uri = UriBuilder.fromUri(base).segment(id).build();

    			final int code;
    			if (TRACKS.containsKey(track.id))
    			{
    				code = HttpServletResponse.SC_NO_CONTENT;
    				updated++;
    			}
    			else
    			{
    				code = HttpServletResponse.SC_CREATED;
    				inserted++;
    			}

    			TRACKS.put(track.id, track);

    			resultDetails[resultIndex++] = ResultDetailsDTO.create(
    				code,
    				null,
    				String.valueOf(track.id),
    				uri);
    		}

    		if (inserted == 1 && updated == 0)
    			return Response.created(resultDetails[0].details).build();

    		return Response.ok().entity(resultDetails).build();
    	}

    	/**
    	 * Updates one track.
    	 */
    	@PUT
    	@Path("/tracks/{id}")
    	@Documentation("Update one track")
    	public void handleUpdateOneTrack(@PathParam("id") Integer id, TrackDTO aTrack)
    	{
    		final TrackDTO track = TRACKS.get(id);
    		if (track == null)
    			throw new NotFoundException("Track id [" + id + "] does not found.");

    		if (aTrack.id != null && !aTrack.id.equals(track.id))
    			throw new BadRequestException("Selected track id [" + id
    				+ "] is not equals to body track id.");

    		TRACKS.put(aTrack.id, aTrack);
    	}
    }

This REST service uses the following Java classes, which represent a Data Transfer Objects (DTO), to serialize and deserialize data:

    /**
     * DTO for a track.
     */
    public final class TrackDTO
    {
    	public Integer id;
    	public String singer;
    	public String title;
    }

    import java.net.*;

    /**
     * DTO for result details.
     */
    @JsonbPropertyOrder({ "code", "label", "foreignKey", "details" })
    public final class ResultDetailsDTO
    {
    	public int code;
    	public String label;
    	public String foreignKey;
    	public URI details;

    	public static ResultDetailsDTO create(
    		final int aCode,
    		final String aForeignKey,
    		final URI aDetails)
    	{
    		return ResultDetailsDTO.create(aCode, null, aForeignKey, aDetails);
    	}

    	public static ResultDetailsDTO create(
    		final int aCode,
    		final String aLabel,
    		final String aForeignKey,
    		final URI aDetails)
    	{
    		final ResultDetailsDTO result = new ResultDetailsDTO();
    		result.code = aCode;
    		result.label = aLabel;
    		result.foreignKey = aForeignKey;
    		result.details = aDetails;
    		return result;
    	}
    }

## Serialization of a table record

### Built-in serializers

Default JSON serializers and deserializers are provided to handle table records when declared in DTOs as `ContentHolder`s. Both extended and compact JSON formats of record are supported.

    /**
     * DTO for a singer.
     */
    public final class SingerDTO
    {
    	@Table(
    		dataModel = "urn:ebx:module:tracks-module:/WEB-INF/ebx/schemas/tracks.xsd",
    		tablePath = "/root/Singers")
    	public ContentHolder content;
    }

A same DTO can be used for serialization and deserialization. In case of serialization, a `ContentHolderForInput` instance will be automatically created and filled with the proper data. Afterwards, this instance will be able to copy its data into a `ValueContextForUpdate`. To deserialize a table records, a `ContentHolderForOutput` must be created in the REST operation JAVA method and returned. The provided `Adaptation` data will then be transformed into a valid peace of JSON and placed into the HTTP response body.

    /**
     * The REST Toolkit Singer service v1.
     */
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    @Path("/singer/v1")
    @Documentation("Singer service")
    public final class SingerService
    {
    	...

    	/**
    	 * Selects a singer by id.
    	 */
    	@GET
    	@Path("/singers/{id}")
    	@Documentation("Selects a singer by id")
    	public SingerDTO handleSelectSingerById(final @PathParam("id") Integer id)
    	{
    		// find the singer adaptation by id
    		final Adaptation singerRecord = ... ;

    		final SingerDTO singerDTO = new SingerDTO();
    		singerDTO.content = ContentHolderForOutput.createForRecord(singerRecord);
    		return singerDTO;
    	}

    	/**
    	 * Inserts one singer.
    	 */
    	@POST
    	@Path("/singers")
    	@Documentation("Inserts one singer")
    	public void handleInsertOneSinger(final SingerDTO aSingerDTO)
    	{
    		final ProgrammaticService svc = ... ;
    		final AdaptationTable singersTable = ... ;
    		final ProcedureResult procedureResult = svc.execute(
    				(aContext) -> {
    					final ValueContextForUpdate createContext = aContext.getContextForNewOccurrence(singersTable); ;
    					aSingerDTO.content.asContentHolderForInput().copyTo(createContext);
    					aContext.doCreateOccurrence(createContext, singersTable);
    				});

    		if (procedureResult.hasFailed())
    			throw new UnprocessableEntityException(
    				procedureResult.getException().getLocalizedMessage());
    	}

    	/**
    	 * updates one singer.
    	 */
    	@PUT
    	@Path("/singers/{id}")
    	@Documentation("updates one singer")
    	public void handleUpdateOneSinger(@PathParam("id") final Integer id, final SingerDTO aSingerDTO)
    	{
    		final ProgrammaticService svc = ... ;
    		final AdaptationTable singersTable = ... ;
    		final ProcedureResult procedureResult = svc.execute(
    				(aContext) -> {
    					// find the singer adaptation by id
    					final Adaptation singerRecord = ... ;

    					if (singerRecord == null)
    						throw new NotFoundException("Singer with the id ["+ id + "] has not been found.");

    					final ValueContextForUpdate createContext = aContext.getContext(singerRecord.getAdaptationName()); ;
    					aSingerDTO.content.asContentHolderForInput().copyTo(createContext);
    					aContext.doModifyContent(singerRecord, createContext);
    				});

    		if (procedureResult.hasFailed()){
    			final Exception ex = procedureResult.getException();
    			final Throwable cause = ex.getCause();
    			if(cause instanceof NotFoundException)
    				throw (NotFoundException) cause;

    			throw new UnprocessableEntityException(ex.getLocalizedMessage());
    		}
    	}
    }

The default JSON format of the responses, only composed of business data fields, is called compact.

    {
      "singer":{
      	"firstname":"Frank",
      	"lastname":"Sinatra"
      }
    }

To add technical fields or metadata, the `ExtendedOutput` annotation must be placed over the `ContentHolder` field. The annotation must declare every wished options or the ALL one.

    /**
     * DTO for a singer with technical fields.
     */
    public final class SingerWithTechnicalsDTO
    {
    	@Table(
    		dataModel = "urn:ebx:module:tracks-module:/WEB-INF/ebx/schemas/tracks.xsd",
    		tablePath = "/root/Singers")
    	@ExtendedOutput({Include.LABEL,Include.TECHNICALS,Include.CONTENT})
    	public ContentHolder content;
    }

    {
      "singer":{
       	"label": "23",
       	"creationDate": "2018-09-04T15:35:10.706",
        "creationUser": "user",
        "lastUpdateDate": "2018-10-02T17:05:47.090",
        "lastUpdateUser": "user",
      	"content":{
      		"firstname":{
      			"content":"Frank",
      			"label":"First Name"
      		},
      		"lastname":{
      			"content":"Sinatra",
      			"label":"Last Name"
      		}
     	  }
      }
    }

To use the extended JSON format for input HTTP requests the `ExtendedInput` annotation must be placed over the `ContentHolder` field.

    /**
     * DTO for a singer using the extended JSON format.
     */
    public final class SingerExtendedInputDTO
    {
    	@Table(
    		dataModel = "urn:ebx:module:tracks-module:/WEB-INF/ebx/schemas/tracks.xsd",
    		tablePath = "/root/Singers")
    	@ExtendedInput
    	public ContentHolder content;
    }

### Custom serializers

Since TIBCO EBX® is based on JSON-B (JSR-367), custom serializers and deserializers can be defined through JsonbTypeSerializer and JsonbTypeDeserializer annotations.

By default, `java.math.BigDecimal` is serialized into a JSON `String`. To override this behaviour, implement a custom serializer to a JSON `Number`.

    /**
     * Serializes decimal values in numbers.
     */
    public final class BigDecimalSerializerInJsonNumber implements JsonbSerializer<BigDecimal>
    {
    	@Override
    	public void serialize(
    		final BigDecimal aBigDecimal,
    		final JsonGenerator aJsonGenerator,
    		final SerializationContext aSerializationContext)
    	{
    		if (aBigDecimal == null)
    			aJsonGenerator.writeNull();
    		else
    			aJsonGenerator.write(aBigDecimal);
    	}
    }

    /**
     * DTO for a vynil.
     */
    public final class VinylDTO
    {
    	@JsonbTypeSerializer(BigDecimalSerializerInJsonNumber.class)
    	public BigDecimal price;

    	@JsonbTypeSerializer(CustomTrackDtoSerializer.class)
    	@JsonbTypeDeserializer(CustomTrackDtoDeserializer.class)
    	public TrackDTO track;
    }

## Authentication and lookup mechanism

A custom REST service developed with REST Toolkit supports the same authentication methods and lookup mechanism as the built-in REST data services. However, there is a slight difference concerning the 'Anonymous authentication Scheme' since its scope can be wider by using the ``AnonymousAccessEnabled``. See REST authentication and permissions for more information.

##### See also

- Authentication
- Lookup mechanism

## REST authentication and permissions

By default, every REST resource Java method requires users to be authenticated.

However, some methods may need to be accessible anonymously. These methods must be annotated by ``AnonymousAccessEnabled``.

Some methods may need to be restricted to given profiles. These methods may be annotated by ``Authorization`` to specify an authorization rule. An authorization rule is a Java class that implements the ``AuthorizationRule`` interface.

    import jakarta.ws.rs.*;

    import com.orchestranetworks.rest.annotation.*;

    /**
     * The REST Toolkit service v1.
     */
    @Path("/service/v1")
    @Documentation("Service")
    public final class Service
    {
    	...

    	/**
    	 * Gets service description
    	 */
    	@GET
    	@AnonymousAccessEnabled
    	public String handleServiceDescription()
    	{
    		...
    	}

    	/**
    	 * Gets restricted service
    	 */
    	@GET
    	@Authorization(IsUserAuthorized.class)
    	public RestrictedServiceDTO handleRestrictedService()
    	{
    		...
    	}
    }

## URI builders

REST Toolkit provides an utility interface ``URIInfoUtility`` to generate URIs. An instance of this interface is accessible through the injectable built-in object ``SessionContext``.

### URI builders for built-in

Several URI builders interfaces have been designed to ease built-in RESTful services URI build. Each interface constitute an aggregation of methods related to the same functional concept. This division allows development of modular and generic algorithms. Some of these interfaces are themselves aggregation of other ones, leading to intuitive use of the builders since only consistent combination of method calls are allowed. For example, a URI builder configured for the REST `hierarchy` category will not allow calls to record URI build methods, since record access are part of the REST `data` category concept.

Moreover, these URI builders are preconfigured according to:

- the data from the incoming HTTP request which constitute the base configuration,

- the Module public path prefix which prepends the EBX® module segment, if it exists,

- the data services lineage setting which overrides the whole previous configuration, if it exists,

- The REST forward mode which overrides partially the previous configuration since only a peace of the URI's path is kept, if it is activated.

See ``URIBuilderForBuiltin`` and ``CategoryURIBuilder`` for more information.

### URI builders for resources

URI builders to generate resource access URI are also available. Since resources are seen as only one functional concept, every methods have been defined in the same interface.

Like the URI builder for built-in data services, these ones are already preconfigured according to:

- the data from the incoming HTTP request which constitute the base configuration,

- the external resources settings which overrides all or part of the previous configuration, if they exist,

- the Module public path prefix which prepends the EBX® module segment, if it exists.

### URI builders for server and application

When URIs to services or resources not covered by the previous builders must be build, the default preconfigured URI builders should be used. There are two default URI builders which only differ in their ending path segment. The first one end its path at the server's segment (just before the EBX® module segment) and the second at the REST application's last segment (defined in the `@ApplicationPath` annotation).

These default URI builders are already preconfigured according to:

- the data from the incoming HTTP request which constitute the base configuration,

- the Module public path prefix which prepends the EBX® module segment, if it exists.

## Exception handling

A REST Toolkit Java method can produce a standard HTTP error response by throwing a Java exception that extends the JAX-RS class `jakarta.ws.rs.WebApplicationException`. JAX-RS defines exceptions for various HTTP status codes. EBX® defines ``UnprocessableEntityException`` that adds support for the HTTP `422`(Unprocessable entity) code.

Plain Java exceptions are mapped to the HTTP status code `500` (Internal server error).

If a custom REST service declares an `"application/json"` media type in the `@Produces` Java annotation, then the exception content is returned as shown in the example below. For any other supported media type, no response body is returned and the REST services logging category generates an associated warning message.

    {
      "code": 999,                      // JSON Number, HTTP error code or EBX® error code
                                        // in case of HTTP error 422 (optional)
      "errors": [                       // Additional messages to give details (optional).
        {
          "message": "Message 1"        // JSON String
        },
        {
          "message": "Message 2"
        }
      ]
    }

##### See also

- Media Types

The REST Toolkit provides an ExceptionMapper implementation, but you can replace it with your own implementation as shown in the example below.

    import javax.annotation.*;
    import javax.ws.rs.core.*;
    import javax.ws.rs.ext.*;

    @Provider
    @Priority(4999)
    public class CustomExceptionMapper implements ExceptionMapper<Throwable>
    {
    	@Override
    	public Response toResponse(final Throwable anException)
    	{
    		...
    	}
    }

You can manage a particular method by directly returning the JAX-RS Response.

##### See also

- `ApplicationConfigurator.register`

## Monitoring

REST Toolkit events monitoring is similar to the data services log configuration. The difference is the property key which must be `ebx.log4j.category.log.restServices`.

##### See also

- Monitoring
- Configuring the EBX® logs

Some additional properties are available to configure the log messages. See Configuring REST toolkit services for further information.

## Packaging and registration

All applications and components are required to be packaged into the module's Web Application (war file).

The JAX-RS libraries, except the JAX-RS client API, are already included in `ebx.jar` and must not be packaged in the war file.

See Jakarta EE deployment for more information.

The registration of a REST Toolkit application is integrated into the EBX® module registration process. The registration class must extend ``ModuleRegistrationListener``, declare the Servlet 3.0 annotation `@WebListener` and override the `handleContextInitialized` method.

See Module registration for more information.

    import jakarta.servlet.annotation.*;

    import com.orchestranetworks.module.*;

    @WebListener
    public final class RegistrationModule extends ModuleRegistrationListener
    {
    	@Override
    	public void handleContextInitialized(final ModuleInitializedContext aContext)
    	{
    		// Registers dynamically a REST Toolkit application.
    		aContext.registerRESTApplication(RESTApplication.class);
    	}
    }

## OpenAPI

### Overview

The OpenAPI documents, generated through the TIBCO EBX® native integration, comply with the OpenAPI V3 specification . By structuring and describing the available REST resources of a REST Toolkit application and associated the operations, These documents facilitate their development and consumption. However, only the JSON format is currently supported.

### Activation

To activate the OpenAPI documents generation:

- The ``OpenApiApplicationPath`` annotation must be set on the REST Toolkit application class to document. This annotation requires the definition of a unique path used to configure the OpenAPI operations' base URI.

  ##### Note

  The OpenAPI application path will be followed by the generator version (i.e. `/{open-api-application-path}/v1`). It is recommended to not use paths like `/open-api` as there will be confusion between the generator version and the open-api specification version.

- The ``OpenApiResource`` annotation must be set on the REST Toolkit class resources to consider during the documentation process.

### Configuration

The parameter `ebx.restservices.openApi.unpublished.applications` can be used to not publish the OpenAPI endpoints on a specific environment.

    ################################################################
    # Comma-separated list of canonical application class names
    # whose OpenAPI descriptions will not be published.
    ################################################################
    ebx.restservices.openApi.unpublished.applications=com.example.RestApplication

The parameter `ebx.restservices.openapi.style.short.name` can be used to simplify the names of the generated schemas.

    ################################################################
    # If true, only the simple name of the DTO classes will be used to name the OpenAPI schemas.
    # It also removes the DTO and Dto suffixes at the end of the class name.
    # Default value is false.
    # Note that this may cause conflicts between DTOs with the same name under different packages.
    # It is not recommended, when set to true, to use the underscore character '_' in @Schema
    # annotations since it cause calculation errors.
    ################################################################
    ebx.restservices.openapi.style.short.name=true

### Operations

The OpenAPI operations use the GET or POST HTTP method to generate the JSON document of an entire application or a single service.

The URL format for application is:

> `http[s]://<host>[:<port>]/<open-api-path>/v1`

The URL format for a single service is:

> `http[s]://<host>[:<port>]/<open-api-application-path>/v1/{resourcePath: [^:]*}`

Where:

- `<open-api-application-path>` corresponds to the unique path mentioned in the ``OpenApiApplicationPath`` annotation.

- `{resourcePath: [^:]*}` corresponds to the REST resource's path to document.

#### HTTP codes

HTTP code | Description
--- | ---
`200`(OK) | The document has been successfully generated.
`401`(Unauthorized) | Authentication has failed.
`403`(Forbidden) | The specified resource read permission has been denied to the current user.
`404`(Not found) | The resource, specified in the URL, cannot be found.

### Enhance documentation

#### MicroProfile OpenAPI

In addition to the JAX-RS annotations that will be processed, by default, to generate the OpenAPI documents, TIBCO EBX® supports the Microprofile OpenAPI annotations. These annotations are used to enhance the description without needing to rewrite portions of the OpenAPI document that are already covered by the REST Toolkit framework. The annotations can be used on both application and resource classes depending on their type.

##### Note

If not explicitly defined on a resource class, the OpenAPIDefinition annotation will be inherited from its REST Toolkit application class. This annotation describes the root document object of the OpenAPI document.

#### Serialization of a table record

There are further actions to take when a ``ContentHolder`` is used in a DTO to serialize a table record.

- The `Table` annotation must be set on the field.

- The Schema annotation must tag the field for processing.

  ##### Note

  The `ref` value of the Schema annotation must be non empty and unique in the whole application scope.

    /**
     * DTO for a singer.
     */
    public final class SingerDTO
    {
    	@Table(
    			dataModel = "urn:ebx:module:tracks-module:/WEB-INF/ebx/schemas/tracks.xsd",
    			tablePath = "/root/Singers")
    	@Schema(ref = "singer")
    	public ContentHolder content;
    }

##### Note

Note that due to a limitation the metadata field is not added in this case.

### Permissions

In development run mode, anonymous access to OpenAPI endpoints is granted to any user. In production and integration run modes, the OpenAPI access permissions are inherited from the ones of the underlying resources.

##### See also

- EBX® run mode

- The resource's documentation will only contain the operations accessible by the current user.

- A permission denied will be returned if the current user can not access any operation.

- The REST Toolkit application's documentation will only contain the allowed scopes of resources.

- A permission denied will be returned if the current user can not access any resource.

### Visual documentation

In the development run mode, the Swagger UI is available through the UI tab Administration > Technical configuration > Modules and data models. It allows to view and interact with the documented resources of the corresponding REST Toolkit application. Visual documentation facilitates back-end implementation and client-side consumption.

The Swagger UI is also directly available through the following URLs:

URL format for application OpenAPI visualisation:

> `http[s]://<host>[:<port>]/<open-api-application-path>/v1/ui`

URL format for a single service OpenAPI visualisation:

> `http[s]://<host>[:<port>]/<open-api-application-path>/v1/ui/{resourcePath: [^:]*}`

Where:

- `<open-api-application-path>` corresponds to the unique path mentioned in the ``OpenApiApplicationPath`` annotation.

- `{resourcePath: [^:]*}` corresponds to the REST resource's path to document.

##### Note

When using a reverse proxy or HTTPS configuration, the OpenAPI UI is considered an external resource. This means that external resource URLs must be configured correctly.

## Limitations

OpenAPI | The metadata field is not added on table record serialization. See Serialization of a table record for more information. DTO classes using Java generics that are not lists or maps are not supported.

Documentation > Developer Guide