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);
}
}
}