OpenAPI specs

OpenAPI specs provided by the Cloud SPI

The CMJ Cloud SPI provides all API definitions via OpenAPI specs. This will allow you to use many available tools to generate client and server code in your chosen language and validate and test implementations.

We currently provide OpenAPI specs for:

Backward compatibility

We’re keeping the APIs between minor versions backward compatible. Breaking changes (if needed) will be introduced in major versions, and apps will be able to choose when to migrate to a new major version (see apiVersion attribute in SPI descriptor JSON).

Work with enumeration types

Some specs contain enumeration types (enums) - for example, ObjectType in the Operations API. Values may be added to these enums in the future, which will be considered a backward-compatible change.

However, most serialization frameworks in strongly typed languages will require some form of custom deserialization to handle this. Here’s an example of how to achieve it with Jackson:

package com.botronsoft.cmj.cloud.spi.example; import java.io.IOException; import com.botronsoft.cmj.cloud.spi.operation.models.ObjectMapping; import com.botronsoft.cmj.cloud.spi.operation.models.ObjectType; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.TreeNode; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; /** * A custom Jackson StdDeserializer for ObjectMapping which deserializes ObjectMappings as null in case it receives an unsupported * ObjectType. */ public final class ObjectMappingDeserializer extends StdDeserializer<ObjectMapping> { public ObjectMappingDeserializer() { super(ObjectMapping.class); } public ObjectMapping deserialize(JsonParser parser, DeserializationContext context) throws IOException { TreeNode treeNode = parser.getCodec().readTree(parser); JsonNode node = (JsonNode) treeNode; String sourceId = node.get("sourceId").asText(); String targetId = node.get("targetId").asText(); String typeString = node.get("type").asText(); ObjectMapping result; try { ObjectType type = ObjectType.valueOf(typeString); result = new ObjectMapping(type, sourceId, targetId); } catch (IllegalArgumentException ex) { result = null; } return result; } }
package com.botronsoft.cmj.cloud.spi.example; import java.io.IOException; import java.util.Collection; import java.util.Objects; import java.util.stream.Collectors; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.BeanProperty; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.deser.std.CollectionDeserializer; /** * A custom Jackson CollectionDeserializer which filters out null values from a collection. */ public class NonNullItemsCollectionDeserializer extends CollectionDeserializer { public NonNullItemsCollectionDeserializer(CollectionDeserializer deserializer) { super(deserializer); } @Override public Collection<Object> deserialize(JsonParser parser, DeserializationContext context) throws IOException { Collection<Object> objectCollection = super.deserialize(parser, context); return objectCollection.parallelStream().filter(Objects::nonNull).collect(Collectors.toList()); } @Override public CollectionDeserializer createContextual(DeserializationContext context, BeanProperty property) throws JsonMappingException { return new NonNullItemsCollectionDeserializer(super.createContextual(context, property)); } }
package com.botronsoft.cmj.cloud.spi.example; import com.fasterxml.jackson.databind.BeanDescription; import com.fasterxml.jackson.databind.DeserializationConfig; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier; import com.fasterxml.jackson.databind.deser.std.CollectionDeserializer; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.type.CollectionType; public class NonNullCollectionModule extends SimpleModule { @Override public void setupModule(SetupContext context) { super.setupModule(context); context.addBeanDeserializerModifier(new CustomBeanDeserializerModifier()); } class CustomBeanDeserializerModifier extends BeanDeserializerModifier { @Override public JsonDeserializer<?> modifyCollectionDeserializer(DeserializationConfig config, CollectionType type, BeanDescription beanDesc, JsonDeserializer<?> deserializer) { if (deserializer instanceof CollectionDeserializer) { return new NonNullItemsCollectionDeserializer((CollectionDeserializer) deserializer); } return super.modifyCollectionDeserializer(config, type, beanDesc, deserializer); } } }