diff --git a/src/main/java/uk/ac/sanger/sccp/stan/model/store/BasicLocation.java b/src/main/java/uk/ac/sanger/sccp/stan/model/store/BasicLocation.java index 64fa5d92..e06e6df3 100644 --- a/src/main/java/uk/ac/sanger/sccp/stan/model/store/BasicLocation.java +++ b/src/main/java/uk/ac/sanger/sccp/stan/model/store/BasicLocation.java @@ -1,5 +1,6 @@ package uk.ac.sanger.sccp.stan.model.store; +import com.fasterxml.jackson.annotation.JsonIgnore; import uk.ac.sanger.sccp.stan.model.Address; import java.util.Objects; @@ -16,14 +17,15 @@ public class BasicLocation { private Address address; private Integer addressIndex; private int numStored; + private int numChildren; public BasicLocation() {} public BasicLocation(String barcode, Address address) { - this(barcode, null, address, null, 0); + this(barcode, null, address, null, 0, 0); } - public BasicLocation(String barcode, String name, Address address, Integer addressIndex, int numStored) { + public BasicLocation(String barcode, String name, Address address, Integer addressIndex, int numStored, int numChildren) { this.barcode = barcode; this.name = name; this.address = address; @@ -72,6 +74,20 @@ public void setNumStored(int numStored) { this.numStored = numStored; } + /** The number of locations directly inside this location */ + public int getNumChildren() { + return this.numChildren; + } + + public void setNumChildren(int numChildren) { + this.numChildren = numChildren; + } + + @JsonIgnore + public boolean isLeaf() { + return getNumChildren() == 0; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -81,7 +97,9 @@ public boolean equals(Object o) { && Objects.equals(this.name, that.name) && Objects.equals(this.address, that.address) && Objects.equals(this.addressIndex, that.addressIndex) - && this.numStored==that.numStored); + && this.numStored==that.numStored + && this.numChildren==that.numChildren + ); } @Override diff --git a/src/main/java/uk/ac/sanger/sccp/stan/model/store/LinkedLocation.java b/src/main/java/uk/ac/sanger/sccp/stan/model/store/LinkedLocation.java index cc0eefb2..6085cc04 100644 --- a/src/main/java/uk/ac/sanger/sccp/stan/model/store/LinkedLocation.java +++ b/src/main/java/uk/ac/sanger/sccp/stan/model/store/LinkedLocation.java @@ -1,5 +1,6 @@ package uk.ac.sanger.sccp.stan.model.store; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.google.common.base.MoreObjects; import uk.ac.sanger.sccp.stan.model.Address; @@ -17,6 +18,7 @@ public class LinkedLocation { private String name; private Address address; private int numStored; + private int numChildren; public String getBarcode() { return this.barcode; @@ -105,6 +107,19 @@ public void setNumStored(int numStored) { this.numStored = numStored; } + public int getNumChildren() { + return this.numChildren; + } + + public void setNumChildren(int numChildren) { + this.numChildren = numChildren; + } + + @JsonIgnore + public boolean isLeaf() { + return getNumChildren() == 0; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -117,6 +132,7 @@ protected boolean equalsLinkedLocation(LinkedLocation that) { && Objects.equals(this.name, that.name) && Objects.equals(this.address, that.address) && this.numStored == that.numStored + && this.numChildren == that.numChildren ); } @@ -132,6 +148,7 @@ public String toString() { .add("name", repr(name)) .add("address", address) .add("numStored", numStored) + .add("numChildren", numChildren) .omitNullValues() .toString(); } diff --git a/src/main/java/uk/ac/sanger/sccp/stan/service/store/StoreService.java b/src/main/java/uk/ac/sanger/sccp/stan/service/store/StoreService.java index ff5f109f..a3ba2735 100644 --- a/src/main/java/uk/ac/sanger/sccp/stan/service/store/StoreService.java +++ b/src/main/java/uk/ac/sanger/sccp/stan/service/store/StoreService.java @@ -307,7 +307,8 @@ private UCMap makeBasicLocations(ArrayNode nodes) { } Integer addressIndex = integerFromNode(sd.get("addressIndex")); int numStored = intFromNode(sd.get("numStored"), 0); - map.put(itemBarcode, new BasicLocation(locationBarcode, locationName, address, addressIndex, numStored)); + int numChildren = intFromNode(sd.get("numChildren"), 0); + map.put(itemBarcode, new BasicLocation(locationBarcode, locationName, address, addressIndex, numStored, numChildren)); } return map; } diff --git a/src/main/resources/schema.graphqls b/src/main/resources/schema.graphqls index cdb2f0ae..7a63fbc2 100644 --- a/src/main/resources/schema.graphqls +++ b/src/main/resources/schema.graphqls @@ -904,6 +904,8 @@ type Location { qualifiedNameWithFirstBarcode: String """The number of items directly stored in this location.""" numStored: Int! + """Is this location a leaf (i.e. does not contain other locations)?""" + leaf: Boolean! } """Information about a storage location, without links to other locations and items.""" @@ -918,6 +920,8 @@ type LinkedLocation { address: Address """The number of items directly stored in this location.""" numStored: Int! + """Is this location a leaf (i.e. does not contain other locations)?""" + leaf: Boolean! } """The result of a request to empty a location.""" diff --git a/src/main/resources/storelight/editLocation.graphql b/src/main/resources/storelight/editLocation.graphql index 74c57d06..268f2701 100644 --- a/src/main/resources/storelight/editLocation.graphql +++ b/src/main/resources/storelight/editLocation.graphql @@ -5,10 +5,11 @@ mutation { name address size { numRows numColumns } - children { barcode name address numStored } + children { barcode name address numStored numChildren } stored { barcode address } - parent { barcode name address numStored } + parent { barcode name address numStored numChildren } direction numStored + numChildren } } \ No newline at end of file diff --git a/src/main/resources/storelight/location.graphql b/src/main/resources/storelight/location.graphql index 13267047..72b24ae7 100644 --- a/src/main/resources/storelight/location.graphql +++ b/src/main/resources/storelight/location.graphql @@ -5,11 +5,12 @@ name address size { numRows numColumns} - children { barcode name address numStored } + children { barcode name address numStored numChildren } stored { barcode address } - parent { barcode name address numStored } + parent { barcode name address numStored numChildren } direction qualifiedNameWithFirstBarcode numStored + numChildren } } \ No newline at end of file diff --git a/src/main/resources/storelight/storeBarcode.graphql b/src/main/resources/storelight/storeBarcode.graphql index c75cdf69..808517f0 100644 --- a/src/main/resources/storelight/storeBarcode.graphql +++ b/src/main/resources/storelight/storeBarcode.graphql @@ -8,11 +8,12 @@ mutation { name address size { numRows numColumns } - children { barcode name address numStored } + children { barcode name address numStored numChildren } stored { barcode address } - parent { barcode name address numStored } + parent { barcode name address numStored numChildren } direction numStored + numChildren } } } \ No newline at end of file diff --git a/src/main/resources/storelight/stored.graphql b/src/main/resources/storelight/stored.graphql index fcc838a7..2a36ceb2 100644 --- a/src/main/resources/storelight/stored.graphql +++ b/src/main/resources/storelight/stored.graphql @@ -9,12 +9,13 @@ name address size { numRows numColumns} - children { barcode name address numStored } - parent { barcode name address numStored } + children { barcode name address numStored numChildren } + parent { barcode name address numStored numChildren } stored { barcode address addressIndex } direction qualifiedNameWithFirstBarcode numStored + numChildren } } } diff --git a/src/main/resources/storelight/storedBasicLocation.graphql b/src/main/resources/storelight/storedBasicLocation.graphql index 0467c5b4..2691a97c 100644 --- a/src/main/resources/storelight/storedBasicLocation.graphql +++ b/src/main/resources/storelight/storedBasicLocation.graphql @@ -3,6 +3,6 @@ barcode address addressIndex - location { barcode name numStored } + location { barcode name numStored numChildren } } } \ No newline at end of file diff --git a/src/test/java/uk/ac/sanger/sccp/stan/integrationtest/TestReleaseMutation.java b/src/test/java/uk/ac/sanger/sccp/stan/integrationtest/TestReleaseMutation.java index c80a575e..d6b79e53 100644 --- a/src/test/java/uk/ac/sanger/sccp/stan/integrationtest/TestReleaseMutation.java +++ b/src/test/java/uk/ac/sanger/sccp/stan/integrationtest/TestReleaseMutation.java @@ -109,8 +109,8 @@ public void testRelease() throws Exception { stubStorelightUnstore(mockStorelightClient); UCMap basicLocationMap = new UCMap<>(2); - basicLocationMap.put("STAN-001", new BasicLocation("STO-1", "Box 1", new Address(1,2), 4, 0)); - basicLocationMap.put("STAN-002", new BasicLocation("STO-1", "Box 1", new Address(3,4), null, 0)); + basicLocationMap.put("STAN-001", new BasicLocation("STO-1", "Box 1", new Address(1,2), 4, 0, 0)); + basicLocationMap.put("STAN-002", new BasicLocation("STO-1", "Box 1", new Address(3,4), null, 0, 0)); stubStorelightBasicLocation(mockStorelightClient, basicLocationMap); Object result = tester.post(mutation); diff --git a/src/test/java/uk/ac/sanger/sccp/stan/integrationtest/TestStoreMutations.java b/src/test/java/uk/ac/sanger/sccp/stan/integrationtest/TestStoreMutations.java index 888296f1..ed060d28 100644 --- a/src/test/java/uk/ac/sanger/sccp/stan/integrationtest/TestStoreMutations.java +++ b/src/test/java/uk/ac/sanger/sccp/stan/integrationtest/TestStoreMutations.java @@ -4,6 +4,8 @@ import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.mockito.ArgumentMatchers; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; @@ -208,32 +210,37 @@ public void testTransfer() throws Exception { } @Transactional - @Test - public void testQueryLocation() throws Exception { + @ParameterizedTest + @ValueSource(booleans={false,true}) + public void testQueryLocation(boolean leaf) throws Exception { ObjectMapper objectMapper = new ObjectMapper(); ObjectNode locationNode = objectMapper.createObjectNode() .put("barcode", "STO-A") .put("name", "Alpha: Beta") .putNull("address") .put("numStored", 0) + .put("numChildren", 1) .set("children", objectMapper.createArrayNode() .add(objectMapper.createObjectNode() .put("barcode", "STO-B") .put("name", "Gamma: Delta") .put("address", "B3") - .put("numStored", 5)) + .put("numStored", 5) + .put("numChildren", leaf ? 0 : 3) + ) ); GraphQLResponse graphQLResponse = new GraphQLResponse( objectMapper.createObjectNode().set("location", locationNode), null ); when(mockStorelightClient.postQuery(anyString(), any())).thenReturn(graphQLResponse); Object response = tester.post("query { location(locationBarcode: \"STO-A\") { " + - "barcode fixedName customName address numStored " + - "children { barcode fixedName customName address numStored } } }"); + "barcode fixedName customName address numStored leaf " + + "children { barcode fixedName customName address numStored leaf } } }"); Map loc = chainGet(response, "data", "location"); assertEquals("STO-A", loc.get("barcode")); assertEquals("Alpha", loc.get("fixedName")); assertEquals("Beta", loc.get("customName")); + assertEquals(false, loc.get("leaf")); assertNull(loc.get("address")); assertEquals(0, loc.get("numStored")); assertThat((List) loc.get("children")).hasSize(1); @@ -243,6 +250,7 @@ public void testQueryLocation() throws Exception { assertEquals("Delta", child.get("customName")); assertEquals("B3", child.get("address")); assertEquals(5, child.get("numStored")); + assertEquals(leaf, child.get("leaf")); } @Transactional diff --git a/src/test/java/uk/ac/sanger/sccp/stan/service/TestReleaseService.java b/src/test/java/uk/ac/sanger/sccp/stan/service/TestReleaseService.java index 80ab04cc..3ab2ee08 100644 --- a/src/test/java/uk/ac/sanger/sccp/stan/service/TestReleaseService.java +++ b/src/test/java/uk/ac/sanger/sccp/stan/service/TestReleaseService.java @@ -508,7 +508,7 @@ public void testRecordRelease(String locBarcode, Address address, Integer addres if (locBarcode==null) { loc = null; } else { - loc = new BasicLocation(locBarcode, expectedName, address, addressIndex, 0); + loc = new BasicLocation(locBarcode, expectedName, address, addressIndex, 0, 0); } final int releaseId = 10; diff --git a/src/test/java/uk/ac/sanger/sccp/stan/service/store/TestStoreService.java b/src/test/java/uk/ac/sanger/sccp/stan/service/store/TestStoreService.java index 71d1997d..cb35078a 100644 --- a/src/test/java/uk/ac/sanger/sccp/stan/service/store/TestStoreService.java +++ b/src/test/java/uk/ac/sanger/sccp/stan/service/store/TestStoreService.java @@ -158,10 +158,10 @@ public void testStoreBarcode(boolean withAddress) throws IOException { verifyQueryMatches("mutation { storeBarcode(barcode: \""+itemBarcode+"\", location: {barcode: \"" + locationBarcode+"\"}, address: " + quote(address) + ") { barcode address location " + "{ id barcode name address size { numRows numColumns } " + - "children { barcode name address numStored } " + + "children { barcode name address numStored numChildren } " + "stored { barcode address }" + - "parent { barcode name address numStored }" + - "direction numStored}}}"); + "parent { barcode name address numStored numChildren }" + + "direction numStored numChildren }}}"); verify(service).checkErrors(response); assertEquals(item, result); } @@ -361,10 +361,10 @@ public void testSetLocationCustomName(String name, String oldCustomName, String verifyQueryMatches("mutation { editLocation(location:{barcode:"+json(barcode) +"}, change: {name:"+json(alteredLocation.getName())+"}) {" + "id barcode name address size {numRows numColumns } " + - "children { barcode name address numStored }" + + "children { barcode name address numStored numChildren }" + "stored { barcode address } " + - "parent { barcode name address numStored }" + - "direction numStored }}"); + "parent { barcode name address numStored numChildren }" + + "direction numStored numChildren }}"); verify(service).checkErrors(response); assertEquals(alteredLocation, result); assertEquals(newCustomName, alteredLocation.getCustomName()); @@ -403,12 +403,13 @@ public void testGetLocation() throws IOException { " name" + " address" + " size { numRows numColumns }" + - " children { barcode name address numStored }" + + " children { barcode name address numStored numChildren }" + " stored { barcode address }" + - " parent { barcode name address numStored }" + + " parent { barcode name address numStored numChildren }" + " direction" + " qualifiedNameWithFirstBarcode" + " numStored" + + " numChildren" + " }}", null); @@ -496,12 +497,13 @@ public void testGetStored() throws IOException { " name" + " address" + " size { numRows numColumns}" + - " children { barcode name address numStored }" + - " parent { barcode name address numStored }" + + " children { barcode name address numStored numChildren }" + + " parent { barcode name address numStored numChildren }" + " stored { barcode address addressIndex }" + " direction" + " qualifiedNameWithFirstBarcode" + " numStored" + + " numChildren" + " }}}", null); @@ -544,7 +546,7 @@ public void testLoadBasicLocationsOfItems(boolean succeeds) throws IOException { when(mockClient.postQuery(anyString(), isNull())).thenReturn(response); locations = service.loadBasicLocationsOfItems(stanBarcodes); assertThat(locations).hasSize(2); - assertEquals(new BasicLocation("STO-1", "Box 1", new Address(1,2), 4, 0), locations.get("STAN-1")); + assertEquals(new BasicLocation("STO-1", "Box 1", new Address(1,2), 4, 0, 0), locations.get("STAN-1")); assertEquals(new BasicLocation("STO-2", null), locations.get("STAN-2")); assertNull(locations.get("STAN-3")); verify(service).checkErrors(response); @@ -560,7 +562,7 @@ public void testLoadBasicLocationsOfItems(boolean succeeds) throws IOException { " barcode" + " address" + " addressIndex" + - " location { barcode name numStored }" + + " location { barcode name numStored numChildren }" + "}}", null); }