Skip to content

Commit

Permalink
Add get ADR Revision endpoint (finos#716)
Browse files Browse the repository at this point in the history
  • Loading branch information
grahampacker-ms committed Jan 4, 2025
1 parent 5e5502c commit d3df270
Show file tree
Hide file tree
Showing 7 changed files with 173 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,14 @@ void end_to_end_verify_revisions() {
.body("values[0]", equalTo(1));
}

@Test
@Order(4)
void end_to_end_verify_architecture() {
given()
.when().get("/calm/namespaces/finos/adrs/1/revisions/1")
.then()
.statusCode(200)
.body(equalTo(ADR));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.finos.calm.domain.exception;

/**
* Exception thrown when the specified ADR revision is not found.
*/
public class AdrRevisionNotFoundException extends Exception {
}
39 changes: 37 additions & 2 deletions calm-hub/src/main/java/org/finos/calm/resources/AdrResource.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@
import org.eclipse.microprofile.openapi.annotations.Operation;
import org.finos.calm.domain.Adr;
import org.finos.calm.domain.AdrBuilder;
import org.finos.calm.domain.Architecture;
import org.finos.calm.domain.ValueWrapper;
import org.finos.calm.domain.exception.AdrNotFoundException;
import org.finos.calm.domain.exception.AdrRevisionNotFoundException;
import org.finos.calm.domain.exception.ArchitectureNotFoundException;
import org.finos.calm.domain.exception.ArchitectureVersionNotFoundException;
import org.finos.calm.domain.exception.NamespaceNotFoundException;
import org.finos.calm.store.AdrStore;
import org.slf4j.Logger;
Expand Down Expand Up @@ -100,14 +104,41 @@ public Response getAdrRevisions(@PathParam("namespace") String namespace, @PathP
try {
return Response.ok(new ValueWrapper<>(store.getAdrRevisions(adr))).build();
} catch (NamespaceNotFoundException e) {
logger.error("Invalid namespace [{}] when getting revisions of ADR", adr, e);
logger.error("Invalid namespace [{}] when getting revisions of ADR", namespace, e);
return invalidNamespaceResponse(namespace);
} catch (AdrNotFoundException e) {
logger.error("Invalid ADR [{}] when getting versions of ADR", adr, e);
logger.error("Invalid ADR [{}] when getting versions of ADR", adrId, e);
return invalidAdrResponse(adrId);
}
}

@GET
@Path("{namespace}/adrs/{adrId}/revisions/{revision}")
@Produces(MediaType.APPLICATION_JSON)
@Operation(
summary = "Retrieve a specific revision of an ADR",
description = "Retrieve a specific revision of an ADR"
)
public Response getAdrRevision(@PathParam("namespace") String namespace, @PathParam("adrId") int adrId, @PathParam("revision") int revision) {
Adr adr = AdrBuilder.builder()
.namespace(namespace)
.id(adrId)
.revision(revision)
.build();

try {
return Response.ok(store.getAdrRevision(adr)).build();
} catch (NamespaceNotFoundException e) {
logger.error("Invalid namespace [{}] when getting an ADR", namespace, e);
return invalidNamespaceResponse(namespace);
} catch (AdrNotFoundException e) {
logger.error("Invalid ADR [{}] when getting an ADR revision", adrId, e);
return invalidAdrResponse(adrId);
} catch (AdrRevisionNotFoundException e) {
logger.error("Invalid revision [{}] when getting an ADR", revision, e);
return invalidRevisionResponse(revision);
}
}

private Response adrWithLocationResponse(Adr adr) throws URISyntaxException {
return Response.created(new URI("/calm/namespaces/" + adr.namespace() + "/adrs/" + adr.id() + "/revisions/" + adr.revision())).build();
Expand All @@ -124,4 +155,8 @@ private Response invalidAdrJsonResponse(String adrJson) {
private Response invalidAdrResponse(int adrId) {
return Response.status(Response.Status.NOT_FOUND).entity("Invalid adrId provided: " + adrId).build();
}

private Response invalidRevisionResponse(int revision) {
return Response.status(Response.Status.NOT_FOUND).entity("Invalid revision provided: " + revision).build();
}
}
3 changes: 3 additions & 0 deletions calm-hub/src/main/java/org/finos/calm/store/AdrStore.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import org.finos.calm.domain.Adr;
import org.finos.calm.domain.Architecture;
import org.finos.calm.domain.exception.AdrNotFoundException;
import org.finos.calm.domain.exception.AdrRevisionNotFoundException;
import org.finos.calm.domain.exception.ArchitectureNotFoundException;
import org.finos.calm.domain.exception.ArchitectureVersionNotFoundException;
import org.finos.calm.domain.exception.NamespaceNotFoundException;

import java.util.List;
Expand All @@ -13,5 +15,6 @@ public interface AdrStore {
List<Integer> getAdrsForNamespace(String namespace) throws NamespaceNotFoundException;
Adr createAdrForNamespace(Adr adr) throws NamespaceNotFoundException;
List<Integer> getAdrRevisions(Adr adr) throws NamespaceNotFoundException, AdrNotFoundException;
String getAdrRevision(Adr adr) throws NamespaceNotFoundException, AdrNotFoundException, AdrRevisionNotFoundException;

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
import org.finos.calm.domain.AdrBuilder;
import org.finos.calm.domain.Architecture;
import org.finos.calm.domain.exception.AdrNotFoundException;
import org.finos.calm.domain.exception.AdrRevisionNotFoundException;
import org.finos.calm.domain.exception.ArchitectureNotFoundException;
import org.finos.calm.domain.exception.ArchitectureVersionNotFoundException;
import org.finos.calm.domain.exception.NamespaceNotFoundException;
import org.finos.calm.store.AdrStore;
import org.slf4j.Logger;
Expand Down Expand Up @@ -98,6 +100,29 @@ public List<Integer> getAdrRevisions(Adr adr) throws NamespaceNotFoundException,
throw new AdrNotFoundException();
}

@Override
public String getAdrRevision(Adr adr) throws NamespaceNotFoundException, AdrNotFoundException, AdrRevisionNotFoundException {
Document result = retrieveAdrRevisions(adr);

List<Document> adrs = (List<Document>) result.get("adrs");
for (Document adrDoc : adrs) {
if (adr.id() == adrDoc.getInteger("adrId")) {
// Retrieve the versions map from the matching pattern
Document revisions = (Document) adrDoc.get("revisions");

// Return the ADR JSON blob for the specified version
Document revisionDoc = (Document) revisions.get(String.valueOf(adr.revision()));
log.info("RevisionDoc: [{}], Revision: [{}]", adrDoc.get("revisions"), adr.revision());
if(revisionDoc == null) {
throw new AdrRevisionNotFoundException();
}
return revisionDoc.toJson();
}
}
//ADR Revisions is empty, no version to find
throw new AdrRevisionNotFoundException();
}

private Document retrieveAdrRevisions(Adr adr) throws NamespaceNotFoundException, AdrNotFoundException {
if(!namespaceStore.namespaceExists(adr.namespace())) {
throw new NamespaceNotFoundException();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
import org.finos.calm.domain.AdrBuilder;
import org.finos.calm.domain.Architecture;
import org.finos.calm.domain.exception.AdrNotFoundException;
import org.finos.calm.domain.exception.AdrRevisionNotFoundException;
import org.finos.calm.domain.exception.ArchitectureNotFoundException;
import org.finos.calm.domain.exception.ArchitectureVersionNotFoundException;
import org.finos.calm.domain.exception.NamespaceNotFoundException;
import org.finos.calm.store.AdrStore;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -145,7 +147,7 @@ void return_a_created_with_location_of_adr_when_creating_adr() throws NamespaceN
verify(mockAdrStore, times(1)).createAdrForNamespace(expectedAdrToCreate);
}

static Stream<Arguments> provideParametersForAdrVersionTests() {
static Stream<Arguments> provideParametersForAdrRevisionTests() {
return Stream.of(
Arguments.of("invalid", new NamespaceNotFoundException(), 404),
Arguments.of("valid", new AdrNotFoundException(), 404),
Expand All @@ -154,8 +156,8 @@ static Stream<Arguments> provideParametersForAdrVersionTests() {
}

@ParameterizedTest
@MethodSource("provideParametersForAdrVersionTests")
void respond_correctly_to_get_adr_versions_query(String namespace, Throwable exceptionToThrow, int expectedStatusCode) throws NamespaceNotFoundException, AdrNotFoundException {
@MethodSource("provideParametersForAdrRevisionTests")
void respond_correctly_to_get_adr_revisions_query(String namespace, Throwable exceptionToThrow, int expectedStatusCode) throws NamespaceNotFoundException, AdrNotFoundException {
var revisions = List.of(1, 2);
if (exceptionToThrow != null) {
when(mockAdrStore.getAdrRevisions(any(Adr.class))).thenThrow(exceptionToThrow);
Expand All @@ -179,10 +181,10 @@ void respond_correctly_to_get_adr_versions_query(String namespace, Throwable exc
.statusCode(expectedStatusCode);
}

verifyExpectedAdrForRevisions(namespace);
verifyExpectedAdrRevisions(namespace);
}

private void verifyExpectedAdrForRevisions(String namespace) throws NamespaceNotFoundException, AdrNotFoundException {
private void verifyExpectedAdrRevisions(String namespace) throws NamespaceNotFoundException, AdrNotFoundException {
Adr expectedAdrToRetrieve = AdrBuilder.builder()
.namespace(namespace)
.id(12)
Expand All @@ -191,4 +193,52 @@ private void verifyExpectedAdrForRevisions(String namespace) throws NamespaceNot
verify(mockAdrStore, times(1)).getAdrRevisions(expectedAdrToRetrieve);
}


static Stream<Arguments> provideParametersForGetAdrRevisionTests() {
return Stream.of(
Arguments.of("invalid", new NamespaceNotFoundException(), 404),
Arguments.of("valid", new AdrNotFoundException(), 404),
Arguments.of("valid", new AdrRevisionNotFoundException(), 404),
Arguments.of("valid", null, 200)
);
}

@ParameterizedTest
@MethodSource("provideParametersForGetAdrRevisionTests")
void respond_correctly_to_get_adr_revision(String namespace, Throwable exceptionToThrow, int expectedStatusCode) throws NamespaceNotFoundException, AdrNotFoundException, AdrRevisionNotFoundException {
if (exceptionToThrow != null) {
when(mockAdrStore.getAdrRevision(any(Adr.class))).thenThrow(exceptionToThrow);
} else {
String adr = "{ \"test\": \"json\" }";
when(mockAdrStore.getAdrRevision(any(Adr.class))).thenReturn(adr);
}

if (expectedStatusCode == 200) {
given()
.when()
.get("/calm/namespaces/" + namespace + "/adrs/12/revisions/1")
.then()
.statusCode(expectedStatusCode)
.body(equalTo("{ \"test\": \"json\" }"));
} else {
given()
.when()
.get("/calm/namespaces/" + namespace + "/adrs/12/revisions/1")
.then()
.statusCode(expectedStatusCode);
}

verifyExpectedGetAdrRevision(namespace);
}

private void verifyExpectedGetAdrRevision(String namespace) throws NamespaceNotFoundException, AdrNotFoundException, AdrRevisionNotFoundException {
Adr expectedAdrToRetrieve = AdrBuilder.builder()
.namespace(namespace)
.id(12)
.revision(1)
.build();

verify(mockAdrStore, times(1)).getAdrRevision(expectedAdrToRetrieve);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
import org.finos.calm.domain.AdrBuilder;
import org.finos.calm.domain.Architecture;
import org.finos.calm.domain.exception.AdrNotFoundException;
import org.finos.calm.domain.exception.ArchitectureNotFoundException;
import org.finos.calm.domain.exception.AdrRevisionNotFoundException;
import org.finos.calm.domain.exception.ArchitectureVersionNotFoundException;
import org.finos.calm.domain.exception.NamespaceNotFoundException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -224,6 +225,29 @@ void get_adr_revisions_for_valid_adr_returns_list_of_revisions() throws Namespac
assertThat(adrRevisions, is(List.of(1)));
}

@Test
void throw_an_exception_for_an_invalid_adre_when_retrieving_adr_revision() {
FindIterable<Document> findIterable = setupInvalidAdr();
Adr adr = AdrBuilder.builder().namespace(NAMESPACE).build();

assertThrows(AdrNotFoundException.class,
() -> mongoAdrStore.getAdrRevision(adr));

verify(adrCollection).find(new Document("namespace", adr.namespace()));
verify(findIterable).projection(Projections.fields(Projections.include("adrs")));
}

@Test
void return_an_adr_revision() throws NamespaceNotFoundException, AdrNotFoundException, AdrRevisionNotFoundException {
mockSetupAdrDocumentWithRevisions();

Adr adr = AdrBuilder.builder().namespace(NAMESPACE)
.id(42).revision(1).build();

String adrRevision = mongoAdrStore.getAdrRevision(adr);
assertThat(adrRevision, is(validJson));
}

private void mockSetupAdrDocumentWithRevisions() {
Document mainDocument = setupAdrRevisionDocument();
FindIterable<Document> findIterable = Mockito.mock(FindIterable.class);
Expand All @@ -246,4 +270,15 @@ private Document setupAdrRevisionDocument() {
return new Document("namespace", NAMESPACE)
.append("adrs", Arrays.asList(paddingAdr, targetStoredAdr));
}

@Test
void throw_an_exception_when_revision_of_adr_does_not_exist() {
mockSetupAdrDocumentWithRevisions();

Adr adr = AdrBuilder.builder().namespace(NAMESPACE)
.id(42).revision(9).build();

assertThrows(AdrRevisionNotFoundException.class,
() -> mongoAdrStore.getAdrRevision(adr));
}
}

0 comments on commit d3df270

Please sign in to comment.