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
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Working with the project

### Running mongo
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not saying one needs to use mongo:4.0.

I have an old mac and 4.2 didn't work :D

docker pull mongo:4.0
docker run -d --name mongodb-container -p 27017:27017 -v [AbsolutePath]/mongo/init-mongo.js:/docker-entrypoint-initdb.d/init-mongo.js mongo:4.0

### Running the Server in Development Mode (from this directory)

1. `./mvnw package`
Expand Down
615 changes: 612 additions & 3 deletions mongo/init-mongo.js

Large diffs are not rendered by default.

91 changes: 91 additions & 0 deletions src/main/java/org/finos/calm/domain/Flow.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package org.finos.calm.domain;

import java.util.Objects;

public class Flow {
private final String namespace;
private final int id;
private final String version;
private final String flow;

private Flow(FlowBuilder builder) {
this.namespace = builder.namespace;
this.id = builder.id;
this.version = builder.version;
this.flow = builder.flow;
}

public String getNamespace() {
return namespace;
}

public int getId() {
return id;
}

public String getDotVersion() {
return version;
}

public String getMongoVersion() {
return version.replace('.', '-');
}

public String getFlowJson() {
return flow;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Flow flow1 = (Flow) o;
return id == flow1.id && Objects.equals(namespace, flow1.namespace) && Objects.equals(version, flow1.version) && Objects.equals(flow, flow1.flow);
}

@Override
public int hashCode() {
return Objects.hash(namespace, id, version, flow);
}

@Override
public String toString() {
return "Flow{" +
"namespace='" + namespace + '\'' +
", id=" + id +
", version='" + version + '\'' +
", flow='" + flow + '\'' +
'}';
}

public static class FlowBuilder {
private String namespace;
private int id;
private String version;
private String flow;

public FlowBuilder setNamespace(String namespace) {
this.namespace = namespace;
return this;
}

public FlowBuilder setId(int id) {
this.id = id;
return this;
}

public FlowBuilder setVersion(String version) {
this.version = version;
return this;
}

public FlowBuilder setFlow(String flow) {
this.flow = flow;
return this;
}

public Flow build() {
return new Flow(this);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package org.finos.calm.domain.exception;

public class FlowNotFoundException extends Exception {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package org.finos.calm.domain.exception;

public class FlowVersionExistsException extends Exception {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package org.finos.calm.domain.exception;

public class FlowVersionNotFoundException extends Exception {
}
198 changes: 198 additions & 0 deletions src/main/java/org/finos/calm/resources/FlowResource.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
package org.finos.calm.resources;

import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.openapi.annotations.Operation;
import org.finos.calm.domain.*;
import org.finos.calm.domain.exception.NamespaceNotFoundException;
import org.finos.calm.domain.exception.FlowNotFoundException;
import org.finos.calm.domain.exception.FlowVersionExistsException;
import org.finos.calm.domain.exception.FlowVersionNotFoundException;
import org.finos.calm.store.FlowStore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URI;
import java.net.URISyntaxException;

@Path("/calm/namespaces")
public class FlowResource {

private final FlowStore store;

private final Logger logger = LoggerFactory.getLogger(FlowResource.class);

@ConfigProperty(name = "allow.put.operations", defaultValue = "false")
Boolean allowPutOperations;

public FlowResource(FlowStore store) {
this.store = store;
}

@GET
@Path("{namespace}/flows")
@Produces(MediaType.APPLICATION_JSON)
@Operation(
summary = "Retrieve flows in a given namespace",
description = "Flows stored in a given namespace"
)
public Response getFlowsForNamespace(@PathParam("namespace") String namespace) {
try {
return Response.ok(new ValueWrapper<>(store.getFlowsForNamespace(namespace))).build();
} catch (NamespaceNotFoundException e) {
logger.error("Invalid namespace [{}] when retrieving flows", namespace, e);
return invalidNamespaceResponse(namespace);
}
}

@POST
@Path("{namespace}/flows")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Operation(
summary = "Create flow for namespace",
description = "Creates a flow for a given namespace with an allocated ID and version 1.0.0"
)
public Response createFlowForNamespace(@PathParam("namespace") String namespace, String flowJson) throws URISyntaxException {
Flow flow = new Flow.FlowBuilder()
.setNamespace(namespace)
.setFlow(flowJson)
.build();

try {
return flowWithLocationResponse(store.createFlowForNamespace(flow));
} catch (NamespaceNotFoundException e) {
logger.error("Invalid namespace [{}] when creating flow", namespace, e);
return invalidNamespaceResponse(namespace);
}
}

@GET
@Path("{namespace}/flows/{flowId}/versions")
@Produces(MediaType.APPLICATION_JSON)
@Operation(
summary = "Retrieve a list of versions for a given flow",
description = "Flow versions are not opinionated, outside of the first version created"
)
public Response getFlowVersions(@PathParam("namespace") String namespace, @PathParam("flowId") int flowId) {
Flow flow = new Flow.FlowBuilder()
.setNamespace(namespace)
.setId(flowId)
.build();

try {
return Response.ok(new ValueWrapper<>(store.getFlowVersions(flow))).build();
} catch (NamespaceNotFoundException e) {
logger.error("Invalid namespace [{}] when getting versions of flow", flow, e);
return invalidNamespaceResponse(namespace);
} catch (FlowNotFoundException e) {
logger.error("Invalid flow [{}] when getting versions of flow", flow, e);
return invalidFlowResponse(flowId);
}
}

@GET
@Path("{namespace}/flows/{flowId}/versions/{version}")
@Produces(MediaType.APPLICATION_JSON)
@Operation(
summary = "Retrieve a specific flow at a given version",
description = "Retrieve flows at a specific version"
)
public Response getFlow(@PathParam("namespace") String namespace, @PathParam("flowId") int flowId, @PathParam("version") String version) {
Flow flow = new Flow.FlowBuilder()
.setNamespace(namespace)
.setId(flowId)
.setVersion(version)
.build();

try {
return Response.ok(store.getFlowForVersion(flow)).build();
} catch (NamespaceNotFoundException e) {
logger.error("Invalid namespace [{}] when getting a flow", flow, e);
return invalidNamespaceResponse(namespace);
} catch (FlowNotFoundException e) {
logger.error("Invalid flow [{}] when getting a flow", flow, e);
return invalidFlowResponse(flowId);
} catch (FlowVersionNotFoundException e) {
logger.error("Invalid version [{}] when getting a flow", flow, e);
return invalidVersionResponse(version);
}
}

@POST
@Path("{namespace}/flows/{flowId}/versions/{version}")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response createVersionedFlow(@PathParam("namespace") String namespace, @PathParam("flowId") int flowId, @PathParam("version") String version, String flowJson) throws URISyntaxException {
Flow flow = new Flow.FlowBuilder()
.setNamespace(namespace)
.setId(flowId)
.setVersion(version)
.setFlow(flowJson)
.build();

try {
store.createFlowForVersion(flow);
return flowWithLocationResponse(flow);
} catch (FlowVersionExistsException e) {
logger.error("Flow version already exists [{}] when trying to create new flow", flow, e);
return Response.status(Response.Status.CONFLICT).entity("Version already exists: " + version).build();
} catch (FlowNotFoundException e) {
logger.error("Invalid flow [{}] when getting a flow", flow, e);
return invalidFlowResponse(flowId);
} catch (NamespaceNotFoundException e) {
logger.error("Invalid namespace [{}] when getting a flow", flow, e);
return invalidNamespaceResponse(namespace);
}
}

@PUT
@Path("{namespace}/flows/{flowId}/versions/{version}")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Operation(
summary = "Updates a Flow (if available)",
description = "In mutable version stores flow updates are supported by this endpoint, operation unavailable returned in repositories without configuration specified"
)
public Response updateVersionedFlow(@PathParam("namespace") String namespace, @PathParam("flowId") int flowId, @PathParam("version") String version, String flowJson) throws URISyntaxException {
Flow flow = new Flow.FlowBuilder()
.setNamespace(namespace)
.setId(flowId)
.setVersion(version)
.setFlow(flowJson)
.build();

if (!allowPutOperations) {
return Response.status(Response.Status.FORBIDDEN).entity("This Calm Hub does not support PUT operations").build();
}

try {
store.updateFlowForVersion(flow);
return flowWithLocationResponse(flow);
} catch (NamespaceNotFoundException e) {
logger.error("Invalid namespace [{}] when trying to put flow", flow, e);
return invalidNamespaceResponse(namespace);
} catch (FlowNotFoundException e) {
logger.error("Invalid flow [{}] when trying to put flow", flow, e);
return invalidFlowResponse(flowId);
}
}

private Response flowWithLocationResponse(Flow flow) throws URISyntaxException {
return Response.created(new URI("/calm/namespaces/" + flow.getNamespace() + "/flows/" + flow.getId() + "/versions/" + flow.getDotVersion())).build();
}

private Response invalidNamespaceResponse(String namespace) {
return Response.status(Response.Status.NOT_FOUND).entity("Invalid namespace provided: " + namespace).build();
}

private Response invalidFlowResponse(int flowId) {
return Response.status(Response.Status.NOT_FOUND).entity("Invalid flow provided: " + flowId).build();
}

private Response invalidVersionResponse(String version) {
return Response.status(Response.Status.NOT_FOUND).entity("Invalid version provided: " + version).build();
}
}
19 changes: 19 additions & 0 deletions src/main/java/org/finos/calm/store/FlowStore.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.finos.calm.store;

import org.bson.json.JsonParseException;
import org.finos.calm.domain.*;
import org.finos.calm.domain.exception.NamespaceNotFoundException;
import org.finos.calm.domain.exception.FlowNotFoundException;
import org.finos.calm.domain.exception.FlowVersionExistsException;
import org.finos.calm.domain.exception.FlowVersionNotFoundException;

import java.util.List;

public interface FlowStore {
List<Integer> getFlowsForNamespace(String namespace) throws NamespaceNotFoundException;
Flow createFlowForNamespace(Flow flow) throws NamespaceNotFoundException, JsonParseException;
List<String> getFlowVersions(Flow flow) throws NamespaceNotFoundException, FlowNotFoundException;
String getFlowForVersion(Flow flow) throws NamespaceNotFoundException, FlowNotFoundException, FlowVersionNotFoundException;
Flow createFlowForVersion(Flow flow) throws NamespaceNotFoundException, FlowNotFoundException, FlowVersionExistsException;
Flow updateFlowForVersion(Flow flow) throws NamespaceNotFoundException, FlowNotFoundException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,15 @@ public MongoCounterStore(MongoClient mongoClient) {
this.counterCollection = database.getCollection("counters");
}

public int getNextSequenceValue() {
public int getNextPatternSequenceValue() {
return nextValueForCounter("patternStoreCounter");
}

public int getNextFlowSequenceValue() {
return nextValueForCounter("flowStoreCounter");
}


public int getNextArchitectureSequenceValue() {
return nextValueForCounter("architectureStoreCounter");
}
Expand Down
Loading