diff --git a/src/main/java/co/zeroae/gate/App.java b/src/main/java/co/zeroae/gate/App.java index 6f963f4..4194f18 100644 --- a/src/main/java/co/zeroae/gate/App.java +++ b/src/main/java/co/zeroae/gate/App.java @@ -10,6 +10,7 @@ import com.amazonaws.xray.AWSXRay; import gate.*; +import gate.corpora.DocumentImpl; import gate.util.GateException; import gate.util.persistence.PersistenceManager; @@ -44,6 +45,7 @@ public class App implements RequestHandler()); + final Map queryStringParams = Optional.ofNullable(input.getQueryStringParameters()).orElse(new HashMap<>()); try { final String acceptHeader = input.getHeaders().getOrDefault("Accept", "application/json"); final String responseType = ((Supplier) () -> { @@ -80,12 +83,14 @@ public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent in final FeatureMap featureMap = Factory.newFeatureMap(); + final Integer nextAnnotationId = Integer.parseInt(queryStringParams.getOrDefault("nextAnnotationId", "0")); final String contentType = input.getHeaders().getOrDefault("Content-Type", "text/plain"); final String contentDigest = AWSXRay.createSubsegment("Message Digest",() -> { - String rv = Utils.computeMessageDigest(contentType, input.getBody()); + String rv = Utils.computeMessageDigest(contentType + input.getBody() + nextAnnotationId + DIGEST_SALT); AWSXRay.getCurrentSubsegment().putMetadata("SHA256", rv); return rv; }); + featureMap.put("nextAnnotationId", nextAnnotationId); putRequestBody(featureMap, contentType, contentDigest, input.getBody(), input.getIsBase64Encoded()); response.getHeaders().put("x-zae-gate-cache", "HIT"); @@ -93,6 +98,7 @@ public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent in response.getHeaders().put("x-zae-gate-cache", "MISS"); return execute(featureMap); }); + AWSXRay.beginSubsegment("Gate Export"); AWSXRay.getCurrentSubsegment().putMetadata("Content-Type", response.getHeaders().get("Content-Type")); try { @@ -136,10 +142,18 @@ private void putRequestBody(FeatureMap featureMap, String mimeType, String conte private Document execute(FeatureMap docFeatureMap) throws GateException { AWSXRay.beginSubsegment("Gate Execute"); try { - final Document rv = (Document) Factory.createResource("gate.corpora.DocumentImpl", docFeatureMap); - application.getCorpus().add(rv); + final DocumentImpl rvImpl; + + // Note: The DocumentImpl API does not conform to JavaBeans for the nextAnnotationId method. + // Paragraphs may be annotated right away, so we need to handle that issue. + final int nextAnnotationId = (Integer)docFeatureMap.get("nextAnnotationId"); + docFeatureMap.remove("nextAnnotationId"); + rvImpl = (DocumentImpl) Factory.createResource("gate.corpora.DocumentImpl", docFeatureMap); + rvImpl.setNextAnnotationId(Math.max(nextAnnotationId, rvImpl.getNextAnnotationId())); + + application.getCorpus().add(rvImpl); application.execute(); - return rv; + return rvImpl; } catch (GateException e) { AWSXRay.getCurrentSubsegment().addException(e); throw e; diff --git a/src/main/java/co/zeroae/gate/Utils.java b/src/main/java/co/zeroae/gate/Utils.java index 2ca88f5..d16ff6f 100644 --- a/src/main/java/co/zeroae/gate/Utils.java +++ b/src/main/java/co/zeroae/gate/Utils.java @@ -99,14 +99,11 @@ static Map loadExporters() { return Collections.unmodifiableMap(rv); } - static String computeMessageDigest(String mimeType, String bodyContent) { + static String computeMessageDigest(String message) { try { final String rv; final MessageDigest md = MessageDigest.getInstance("SHA-256"); - if (mimeType != null) - md.update(mimeType.getBytes()); - if (bodyContent != null) - md.update(bodyContent.getBytes()); + md.update(message.getBytes()); rv = Hex.encode(md.digest()); return rv; } catch (NoSuchAlgorithmException e) { diff --git a/src/test/java/co/zeroae/gate/AppTest.java b/src/test/java/co/zeroae/gate/AppTest.java index 32e144e..8307cf7 100644 --- a/src/test/java/co/zeroae/gate/AppTest.java +++ b/src/test/java/co/zeroae/gate/AppTest.java @@ -83,6 +83,21 @@ public void testGateXMLToDocument() throws Exception { assertEquals(input.getBody(), doc.getContent().toString()); } + @Test + public void testNextAnnotationId() { + final int nextAnnotationId = 1000 + new Random().nextInt(1000); + input.withQueryStringParameters(new HashMap<>()) + .getQueryStringParameters() + .put("nextAnnotationId", String.valueOf(nextAnnotationId)); + + final APIGatewayProxyResponseEvent result = app.handleRequest(input, context); + + assertEquals("application/gate+xml", result.getHeaders().get("Content-Type")); + final String resultBody = result.getBody(); + assertNotNull(resultBody); + assertTrue(resultBody.contains("