Follow the Yellow Brick Road!

Principles

  • The source of truth for the API is the OpenAPI specification
  • The OpenAPI OperationId is used to identify an action
  • HTTP request and database operation are separated by the EventBus. This allows to distribute load and/or add additional protocols (e.g. gRPC)
  • We follow a consistent use of REST syntax
  • We value Testing
  • The application provides metrics

Setting Up a Maven Project

Extensions to Project Keep should be in a separate Maven project. Add the following to your pom.xml:

<dependency>
	<groupId>com.hcl.domino.keep</groupId>
	<artifactId>com.hcl.domino.keep</artifactId>
	<version>${keep.core.version}</version>
</dependency>

keep.core.version is here a property mapping to the version of Keep you wish to use. Using properties means you can easily identify and update the versions of dependencies. All properties are grouped at the top of your pom.xml:

<properties>
	<keep.core.version>0.9.1-SNAPSHOT</keep.core.version>
</properties>

Maven Build Settings

There are three plugins you should use as a minimum in your Maven build settings: - maven-jar-plugin jar packaging - maven-surefire-plugin for running tests - maven-dependency-plugin for adding the dependencies to your target folder

<build>
	<plugins>
		<plugin>
			<groupId>org.apache.maven.plugins</groupId>
			<artifactId>maven-jar-plugin</artifactId>
			<version>${maven.jar.plugin.version}</version>
			<configuration>
				<finalName>YOUR-JAR-NAME</finalName>
			</configuration>
		</plugin>
		<plugin>
			<artifactId>maven-surefire-plugin</artifactId>
			<version>${maven.surefire.plugin.version}</version>
			<configuration>
				<includes>
					<include>*Test.java</include>
					<include>*Tests.java</include>
					<include>*TestCase.java</include>
				</includes>
			</configuration>
		</plugin>
		<plugin>
			<groupId>org.apache.maven.plugins</groupId>
			<artifactId>maven-dependency-plugin</artifactId>
			<version>${maven.dependency.plugin.version}</version>
			<executions>
				<execution>
					<id>copy-dependencies</id>
					<phase>package</phase>
					<goals>
						<goal>copy-dependencies</goal>
					</goals>
					<configuration>
						<outputDirectory>${project.build.directory}/libs</outputDirectory>
						<overWriteReleases>false</overWriteReleases>
						<overWriteSnapshots>true</overWriteSnapshots>
					</configuration>
				</execution>
			</executions>
		</plugin>
	</plugins>
</build>

Again, this uses properties for the plugin versions. A special variable ${project.build.directory}, will map to your target output folder. Full releases with no version number change will not be overwritten, but snapshots will.

Ensure you change YOUR-PROJECT-NAME to the jar name you want to use for your extension. Internally we use “keep-experimental” for experimental code, so avoid that.

OpenAPI Spec

You should create an OpenAPI specification for your extensions. Apicurio is a good tool for building your OpenAPI spec. It gives a GUI for building an OpenAPI, as well as a source view for copying and pasting in. Follow these rules:

  • Set up “Data Types”. As a minimum, you’ll need errormessage, executionrequest and executionresult. These can be copied from the main Keep OpenAPI spec.
  • Set up the default “Responses”. These can be copied from the main OpenAPI spec.
  • Set up “Security Schemes”. These can be copied from the main OpenAPI spec.
  • Set up “Servers” for testing. Ensure the first entry starts {protocol}://{yourserver}:{port}/ and should be followed by a unique API prefix and version number. This will be used in the Keep configuration.
  • Set up “Tag Definitions”. These should be unique and will be used to cross-reference against the config, for locating the relevant classes.

config.json

You will need to set up your config.json for this version in the config.d directory. For more details, see configuration. An example is:

{
	"versions": {
		"experimental-v0": {
			"path": "/openapi.exp.v0.json",
			"active": true
		}
	},
	"verticles": {
		"Experimental": {
			"worker": false,
			"className": "com.hcl.domino.keep.DominoDefaultVerticle",
			"tags": {
				"experimental": "com.hcl.domino.keep.experimental"
			},
			"active": true
		},
		"RestAPI": {
			"className": "com.hcl.domino.keep.HttpListener",
			"worker": true,
			"versions": {
				"experimental-v0": {
					"package": "com.hcl.domino.keep.handlers.experimentalv0",
					"route": "/api/exp-v0",
					"defaultClass": "com.hcl.domino.keep.handlers.v1.DefaultJsonHandler",
					"defaultDatabase": "keepconfig",
					"jsonBodyLimit": 5000000,
					"filesBodyLimit": 10000000
				}
			}
		}
	}
}
  • versions: defines a version name for this OpenAPI, marks it active and gives the path to find in the jar to find the .json OpenAPI spec file. E.g. here it would look in src/main/resources/openapi.exp.v0.json. Ensuring a version number allows you to make breaking changes and route to different Java classes for each version.
  • verticles: defines a unique name for managing the NSFHandler verticle for this OpenAPI spec. Typically it does not need to be a worker thread and typically the class name to use for the verticle will be “com.hcl.domino.keep.DominoDefaultVerticle”. You then define the tags to look for in the OpenAPI spec and the package in which to look for the NSFHandler classes.
  • RestAPI allows you to merge settings for the WebHandlers. You should not need to override the HttpListener class. But if you do, you can define the class to use here. The “route” defines the path to append to the server and port prior to the paths specified in the OpenAPI spec. A default database can be defined, if no “db” query parameter is passed. And you can manage maximum body size for ContentType as “application/json” and “multipart/form-data” (for files).

Running and Testing Your Code

You can run and test your code using a Keep Docker container. See the Docker documentation page. The first -v mapping still maps "/local/notesdata/ to the persistent volume for your Domino data. But the second one should map "/opt/hcl/keep/ to the target directory of your Keep extension project, e.g. “-v /Users/paulwithers/GitRepositories/hcl/keep/keep-experimental/target:/opt/hcl/keep”.