diff --git a/.gitignore b/.gitignore index f0d200b3e..ec569ece7 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ node_modules *.tmp !console/src/test/resources/test.zip websocket-api/src/coverage +.settings diff --git a/actions/build.gradle b/actions/build.gradle index ce7ed30c2..d4e6dada2 100644 --- a/actions/build.gradle +++ b/actions/build.gradle @@ -17,6 +17,5 @@ dependencies { test { failFast = true - useJUnitPlatform() - exclude 'com/formkiq/module/actions/awstest/**' + useJUnitPlatform() } \ No newline at end of file diff --git a/actions/src/main/java/com/formkiq/module/actions/ActionType.java b/actions/src/main/java/com/formkiq/module/actions/ActionType.java index e37634c8a..7246365f7 100644 --- a/actions/src/main/java/com/formkiq/module/actions/ActionType.java +++ b/actions/src/main/java/com/formkiq/module/actions/ActionType.java @@ -31,6 +31,8 @@ public enum ActionType { /** AntiVirus. */ ANTIVIRUS, + /** Document Tagging. */ + DOCUMENTTAGGING, /** Full Text. */ FULLTEXT, /** OCR. */ diff --git a/aws-cognito/build.gradle b/aws-cognito/build.gradle index ee1b4984b..d6e403f60 100644 --- a/aws-cognito/build.gradle +++ b/aws-cognito/build.gradle @@ -2,9 +2,9 @@ description = "AWS Cognito" dependencies { - api group: 'software.amazon.awssdk', name: 'cognitoidentity', version: '2.19.2' - api group: 'software.amazon.awssdk', name: 'cognitoidentityprovider', version: '2.19.2' - implementation group: 'software.amazon.awssdk', name: 'url-connection-client', version: '2.19.2' + api group: 'software.amazon.awssdk', name: 'cognitoidentity', version: '2.20.33' + api group: 'software.amazon.awssdk', name: 'cognitoidentityprovider', version: '2.20.33' + implementation group: 'software.amazon.awssdk', name: 'url-connection-client', version: '2.20.33' configurations.all { exclude group: 'software.amazon.awssdk', module: 'apache-client' diff --git a/aws-dynamodb/build.gradle b/aws-dynamodb/build.gradle index ee83edafb..9e2f8e395 100644 --- a/aws-dynamodb/build.gradle +++ b/aws-dynamodb/build.gradle @@ -2,10 +2,11 @@ description = "AWS DynamoDB" dependencies { - api group: 'software.amazon.awssdk', name: 'dynamodb', version: '2.19.2' + api group: 'software.amazon.awssdk', name: 'dynamodb', version: '2.20.33' implementation project(':fkq-lambda-services') - implementation group: 'software.amazon.awssdk', name: 'url-connection-client', version: '2.19.2' - implementation group: 'com.formkiq', name: 'graalvm-annotations', version: '1.1.0' + implementation project(':aws-xray') + implementation group: 'software.amazon.awssdk', name: 'url-connection-client', version: '2.20.33' + implementation group: 'com.formkiq', name: 'graalvm-annotations', version: '1.2.0' testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version:'5.9.1' diff --git a/aws-dynamodb/config/checkstyle/import-control.xml b/aws-dynamodb/config/checkstyle/import-control.xml index 7703ec559..b73b03040 100644 --- a/aws-dynamodb/config/checkstyle/import-control.xml +++ b/aws-dynamodb/config/checkstyle/import-control.xml @@ -19,7 +19,11 @@ + + + + diff --git a/aws-dynamodb/src/main/java/com/formkiq/aws/dynamodb/AttributeValueToDynamicObject.java b/aws-dynamodb/src/main/java/com/formkiq/aws/dynamodb/AttributeValueToDynamicObject.java index 4c4bda528..26e399660 100644 --- a/aws-dynamodb/src/main/java/com/formkiq/aws/dynamodb/AttributeValueToDynamicObject.java +++ b/aws-dynamodb/src/main/java/com/formkiq/aws/dynamodb/AttributeValueToDynamicObject.java @@ -24,8 +24,10 @@ package com.formkiq.aws.dynamodb; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.function.Function; +import java.util.stream.Collectors; import software.amazon.awssdk.services.dynamodb.model.AttributeValue; /** @@ -41,10 +43,20 @@ public DynamicObject apply(final Map map) { DynamicObject o = new DynamicObject(new HashMap<>()); for (Map.Entry e : map.entrySet()) { - String s = e.getValue().s(); - String n = e.getValue().n(); - String v = s == null && n != null ? n : s; - o.put(e.getKey(), v); + + if (e.getValue().hasL()) { + + List values = + e.getValue().l().stream().map(s -> s.s()).collect(Collectors.toList()); + o.put(e.getKey(), values); + + } else { + + String s = e.getValue().s(); + String n = e.getValue().n(); + String v = s == null && n != null ? n : s; + o.put(e.getKey(), v); + } } return o; diff --git a/aws-dynamodb/src/main/java/com/formkiq/aws/dynamodb/DbKeys.java b/aws-dynamodb/src/main/java/com/formkiq/aws/dynamodb/DbKeys.java index b304e0f85..3e9436bbe 100644 --- a/aws-dynamodb/src/main/java/com/formkiq/aws/dynamodb/DbKeys.java +++ b/aws-dynamodb/src/main/java/com/formkiq/aws/dynamodb/DbKeys.java @@ -70,6 +70,12 @@ public interface DbKeys { /** Config Partition Key Prefix. */ String PREFIX_CONFIG = "configs" + TAG_DELIMINATOR; + /** API Keys Partition Key Prefix. */ + String PREFIX_API_KEYS = "apikeys" + TAG_DELIMINATOR; + + /** API Key Partition Key Prefix. */ + String PREFIX_API_KEY = "apikey" + TAG_DELIMINATOR; + /** Documents Partition Key Prefix. */ String PREFIX_DOCS = "docs" + TAG_DELIMINATOR; diff --git a/aws-dynamodb/src/main/java/com/formkiq/aws/dynamodb/DynamoDbConnectionBuilder.java b/aws-dynamodb/src/main/java/com/formkiq/aws/dynamodb/DynamoDbConnectionBuilder.java index 16a3454d3..6822953d1 100644 --- a/aws-dynamodb/src/main/java/com/formkiq/aws/dynamodb/DynamoDbConnectionBuilder.java +++ b/aws-dynamodb/src/main/java/com/formkiq/aws/dynamodb/DynamoDbConnectionBuilder.java @@ -24,9 +24,11 @@ package com.formkiq.aws.dynamodb; import java.net.URI; +import com.amazonaws.xray.interceptors.TracingInterceptor; import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider; import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration.Builder; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; import software.amazon.awssdk.services.dynamodb.DynamoDbClientBuilder; @@ -45,12 +47,19 @@ public class DynamoDbConnectionBuilder { /** * constructor. + * + * @param enableAwsXray Enables AWS X-Ray */ - public DynamoDbConnectionBuilder() { + public DynamoDbConnectionBuilder(final boolean enableAwsXray) { System.setProperty("software.amazon.awssdk.http.service.impl", "software.amazon.awssdk.http.urlconnection.UrlConnectionSdkHttpService"); - this.builder = DynamoDbClient.builder() - .overrideConfiguration(ClientOverrideConfiguration.builder().build()); + Builder clientConfig = ClientOverrideConfiguration.builder(); + + if (enableAwsXray) { + clientConfig.addExecutionInterceptor(new TracingInterceptor()); + } + + this.builder = DynamoDbClient.builder().overrideConfiguration(clientConfig.build()); } /** diff --git a/aws-dynamodb/src/main/java/com/formkiq/aws/dynamodb/DynamoDbService.java b/aws-dynamodb/src/main/java/com/formkiq/aws/dynamodb/DynamoDbService.java index 4c449e333..387ef1ab6 100644 --- a/aws-dynamodb/src/main/java/com/formkiq/aws/dynamodb/DynamoDbService.java +++ b/aws-dynamodb/src/main/java/com/formkiq/aws/dynamodb/DynamoDbService.java @@ -23,6 +23,8 @@ */ package com.formkiq.aws.dynamodb; +import java.util.Collection; +import java.util.List; import java.util.Map; import software.amazon.awssdk.services.dynamodb.model.AttributeValue; import software.amazon.awssdk.services.dynamodb.model.QueryResponse; @@ -60,6 +62,14 @@ public interface DynamoDbService { */ Map get(AttributeValue pk, AttributeValue sk); + /** + * Batch Get a number of Keys. + * + * @param keys {@link Collection} + * @return {@link List} + */ + List> getBatch(Collection> keys); + /** * Put DynamoDb Record. * diff --git a/aws-dynamodb/src/main/java/com/formkiq/aws/dynamodb/DynamoDbServiceImpl.java b/aws-dynamodb/src/main/java/com/formkiq/aws/dynamodb/DynamoDbServiceImpl.java index ed274ce01..424b007f3 100644 --- a/aws-dynamodb/src/main/java/com/formkiq/aws/dynamodb/DynamoDbServiceImpl.java +++ b/aws-dynamodb/src/main/java/com/formkiq/aws/dynamodb/DynamoDbServiceImpl.java @@ -25,14 +25,19 @@ import static com.formkiq.aws.dynamodb.DbKeys.PK; import static com.formkiq.aws.dynamodb.DbKeys.SK; +import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; import software.amazon.awssdk.services.dynamodb.model.AttributeValue; import software.amazon.awssdk.services.dynamodb.model.AttributeValueUpdate; +import software.amazon.awssdk.services.dynamodb.model.BatchGetItemRequest; +import software.amazon.awssdk.services.dynamodb.model.BatchGetItemResponse; import software.amazon.awssdk.services.dynamodb.model.DeleteItemRequest; import software.amazon.awssdk.services.dynamodb.model.GetItemRequest; import software.amazon.awssdk.services.dynamodb.model.GetItemResponse; +import software.amazon.awssdk.services.dynamodb.model.KeysAndAttributes; import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; import software.amazon.awssdk.services.dynamodb.model.QueryRequest; import software.amazon.awssdk.services.dynamodb.model.QueryResponse; @@ -103,6 +108,20 @@ public Map get(final AttributeValue pk, final AttributeV .consistentRead(Boolean.TRUE).build()).item(); } + @Override + public List> getBatch( + final Collection> keys) { + + Map items = + Map.of(this.tableName, KeysAndAttributes.builder().keys(keys).build()); + + BatchGetItemResponse response = + this.dbClient.batchGetItem(BatchGetItemRequest.builder().requestItems(items).build()); + + List> list = response.responses().get(this.tableName); + return list; + } + @Override public void putItem(final Map attributes) { putItem(this.tableName, attributes); diff --git a/dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/DateUtil.java b/aws-dynamodb/src/main/java/com/formkiq/aws/dynamodb/objects/DateUtil.java similarity index 99% rename from dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/DateUtil.java rename to aws-dynamodb/src/main/java/com/formkiq/aws/dynamodb/objects/DateUtil.java index 52d11fb2d..7bd0ba5f3 100644 --- a/dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/DateUtil.java +++ b/aws-dynamodb/src/main/java/com/formkiq/aws/dynamodb/objects/DateUtil.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package com.formkiq.stacks.dynamodb; +package com.formkiq.aws.dynamodb.objects; import java.text.SimpleDateFormat; import java.time.DateTimeException; diff --git a/dynamodb-documents/src/main/java/com/formkiq/stacks/common/formats/MimeFinder.java b/aws-dynamodb/src/main/java/com/formkiq/aws/dynamodb/objects/MimeFinder.java similarity index 82% rename from dynamodb-documents/src/main/java/com/formkiq/stacks/common/formats/MimeFinder.java rename to aws-dynamodb/src/main/java/com/formkiq/aws/dynamodb/objects/MimeFinder.java index 74c0a6dc7..423440ec6 100644 --- a/dynamodb-documents/src/main/java/com/formkiq/stacks/common/formats/MimeFinder.java +++ b/aws-dynamodb/src/main/java/com/formkiq/aws/dynamodb/objects/MimeFinder.java @@ -21,14 +21,14 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package com.formkiq.stacks.common.formats; +package com.formkiq.aws.dynamodb.objects; -import static com.formkiq.stacks.common.formats.MimeType.MIME_DOCX; -import static com.formkiq.stacks.common.formats.MimeType.MIME_HTML; -import static com.formkiq.stacks.common.formats.MimeType.MIME_JPEG; -import static com.formkiq.stacks.common.formats.MimeType.MIME_JSON; -import static com.formkiq.stacks.common.formats.MimeType.MIME_PDF; -import static com.formkiq.stacks.common.formats.MimeType.MIME_PNG; +import static com.formkiq.aws.dynamodb.objects.MimeType.MIME_DOCX; +import static com.formkiq.aws.dynamodb.objects.MimeType.MIME_HTML; +import static com.formkiq.aws.dynamodb.objects.MimeType.MIME_JPEG; +import static com.formkiq.aws.dynamodb.objects.MimeType.MIME_JSON; +import static com.formkiq.aws.dynamodb.objects.MimeType.MIME_PDF; +import static com.formkiq.aws.dynamodb.objects.MimeType.MIME_PNG; import java.util.Set; /** diff --git a/dynamodb-documents/src/main/java/com/formkiq/stacks/common/formats/MimeType.java b/aws-dynamodb/src/main/java/com/formkiq/aws/dynamodb/objects/MimeType.java similarity index 69% rename from dynamodb-documents/src/main/java/com/formkiq/stacks/common/formats/MimeType.java rename to aws-dynamodb/src/main/java/com/formkiq/aws/dynamodb/objects/MimeType.java index f6783c564..c97028d24 100644 --- a/dynamodb-documents/src/main/java/com/formkiq/stacks/common/formats/MimeType.java +++ b/aws-dynamodb/src/main/java/com/formkiq/aws/dynamodb/objects/MimeType.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package com.formkiq.stacks.common.formats; +package com.formkiq.aws.dynamodb.objects; /** * Supported Conversion Formats. @@ -29,20 +29,44 @@ */ public enum MimeType { - /** text/html. */ - MIME_HTML("text/html"), + /** image/bmp. */ + MIME_BMP("image/bmp"), /** application/vnd.openxmlformats-officedocument.wordprocessingml.document. */ MIME_DOCX("application/vnd.openxmlformats-officedocument.wordprocessingml.document"), - /** application/pdf. */ - MIME_PDF("application/pdf"), - /** image/png. */ - MIME_PNG("image/png"), + /** image/gif. */ + MIME_GIF("image/gif"), + /** text/html. */ + MIME_HTML("text/html"), /** image/jpeg. */ MIME_JPEG("image/jpeg"), + /** image/jpg. */ + MIME_JPG("image/jpg"), /** application/json. */ MIME_JSON("application/json"), + /** application/pdf. */ + MIME_PDF("application/pdf"), + /** image/png. */ + MIME_PNG("image/png"), + /** image/tif. */ + MIME_TIF("image/tif"), + /** image/tiff. */ + MIME_TIFF("image/tiff"), /** Unknown Mime. */ - MIME_UNKNOWN("UNKNOWN"); + MIME_UNKNOWN("UNKNOWN"), + /** image/webp. */ + MIME_WEBP("image/webp"); + + /** + * Is Content Type plain text. + * + * @param contentType {@link String} + * @return boolean + */ + public static boolean isPlainText(final String contentType) { + return contentType != null + && (contentType.startsWith("text/") || "application/json".equals(contentType) + || "application/x-www-form-urlencoded".equals(contentType)); + } /** Content Type. */ private String contentType; @@ -66,14 +90,34 @@ public String getContentType() { } /** - * Is Content Type plain text. + * Get File Extension. * - * @param contentType {@link String} - * @return boolean + * @return {@link String} */ - public static boolean isPlainText(final String contentType) { - return contentType != null - && (contentType.startsWith("text/") || "application/json".equals(contentType) - || "application/x-www-form-urlencoded".equals(contentType)); + public String getExtension() { + int pos = this.contentType.lastIndexOf("/"); + return pos > -1 ? this.contentType.substring(pos + 1).toLowerCase() : null; + } + + /** + * Find {@link MimeType} from Content-Type. + * + * @param ct {@link String} + * @return {@link MimeType} + */ + public static MimeType fromContentType(final String ct) { + + MimeType type = MimeType.MIME_UNKNOWN; + + if (ct != null) { + for (MimeType mt : MimeType.values()) { + if (ct.equals(mt.getContentType())) { + type = mt; + break; + } + } + } + + return type; } } diff --git a/aws-dynamodb/src/test/java/com/formkiq/aws/dynamodb/objects/MimeTypeTest.java b/aws-dynamodb/src/test/java/com/formkiq/aws/dynamodb/objects/MimeTypeTest.java new file mode 100644 index 000000000..13a9c7a5d --- /dev/null +++ b/aws-dynamodb/src/test/java/com/formkiq/aws/dynamodb/objects/MimeTypeTest.java @@ -0,0 +1,45 @@ +/** + * MIT License + * + * Copyright (c) 2018 - 2020 FormKiQ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.formkiq.aws.dynamodb.objects; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Test; + +/** + * + * Unit Test for {@link MimeType}. + * + */ +class MimeTypeTest { + + /** + * Test fromContentType. + */ + @Test + void testFromContentType() { + assertEquals(MimeType.MIME_PNG, MimeType.fromContentType("image/png")); + assertEquals(MimeType.MIME_UNKNOWN, MimeType.fromContentType(null)); + } + +} diff --git a/aws-iam/build.gradle b/aws-iam/build.gradle index e4d4c733c..acef1b2d7 100644 --- a/aws-iam/build.gradle +++ b/aws-iam/build.gradle @@ -2,8 +2,8 @@ description = "AWS IAM" dependencies { - api group: 'software.amazon.awssdk', name: 'iam', version: '2.19.2' - implementation group: 'software.amazon.awssdk', name: 'url-connection-client', version: '2.19.2' + api group: 'software.amazon.awssdk', name: 'iam', version: '2.20.33' + implementation group: 'software.amazon.awssdk', name: 'url-connection-client', version: '2.20.33' configurations.all { exclude group: 'software.amazon.awssdk', module: 'apache-client' diff --git a/aws-lambda/build.gradle b/aws-lambda/build.gradle index 7fab0b143..ed8224515 100644 --- a/aws-lambda/build.gradle +++ b/aws-lambda/build.gradle @@ -2,8 +2,8 @@ description = "AWS Lambda" dependencies { - api group: 'software.amazon.awssdk', name: 'lambda', version: '2.19.2' - implementation group: 'software.amazon.awssdk', name: 'url-connection-client', version: '2.19.2' + api group: 'software.amazon.awssdk', name: 'lambda', version: '2.20.33' + implementation group: 'software.amazon.awssdk', name: 'url-connection-client', version: '2.20.33' configurations.all { exclude group: 'software.amazon.awssdk', module: 'apache-client' diff --git a/aws-s3/build.gradle b/aws-s3/build.gradle index 381037486..4f3472ad3 100644 --- a/aws-s3/build.gradle +++ b/aws-s3/build.gradle @@ -2,9 +2,10 @@ description = "AWS S3" dependencies { - api group: 'software.amazon.awssdk', name: 's3', version: '2.19.2' - implementation group: 'software.amazon.awssdk', name: 'url-connection-client', version: '2.19.2' + api group: 'software.amazon.awssdk', name: 's3', version: '2.20.33' + implementation group: 'software.amazon.awssdk', name: 'url-connection-client', version: '2.20.33' implementation project(':fkq-lambda-services') + implementation project(':aws-xray') configurations.all { exclude group: 'software.amazon.awssdk', module: 'apache-client' diff --git a/aws-s3/config/checkstyle/import-control.xml b/aws-s3/config/checkstyle/import-control.xml index 5470b5b82..2281762e7 100644 --- a/aws-s3/config/checkstyle/import-control.xml +++ b/aws-s3/config/checkstyle/import-control.xml @@ -22,6 +22,8 @@ + + diff --git a/aws-s3/src/main/java/com/formkiq/aws/s3/S3ConnectionBuilder.java b/aws-s3/src/main/java/com/formkiq/aws/s3/S3ConnectionBuilder.java index bcfe24d18..d33e691df 100644 --- a/aws-s3/src/main/java/com/formkiq/aws/s3/S3ConnectionBuilder.java +++ b/aws-s3/src/main/java/com/formkiq/aws/s3/S3ConnectionBuilder.java @@ -24,9 +24,11 @@ package com.formkiq.aws.s3; import java.net.URI; +import com.amazonaws.xray.interceptors.TracingInterceptor; import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider; import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.s3.S3Client; @@ -52,13 +54,22 @@ public class S3ConnectionBuilder { /** * constructor. + * + * @param enableAwsXray Enable AWS X-Ray */ - public S3ConnectionBuilder() { + public S3ConnectionBuilder(final boolean enableAwsXray) { System.setProperty("software.amazon.awssdk.http.service.impl", "software.amazon.awssdk.http.urlconnection.UrlConnectionSdkHttpService"); System.setProperty("aws.s3UseUsEast1RegionalEndpoint", "regional"); - this.builder = S3Client.builder().httpClientBuilder(UrlConnectionHttpClient.builder()) + ClientOverrideConfiguration.Builder clientConfig = ClientOverrideConfiguration.builder(); + + if (enableAwsXray) { + clientConfig.addExecutionInterceptor(new TracingInterceptor()); + } + + this.builder = S3Client.builder().overrideConfiguration(clientConfig.build()) + .httpClientBuilder(UrlConnectionHttpClient.builder()) .credentialsProvider(EnvironmentVariableCredentialsProvider.create()); this.presignerBuilder = S3Presigner.builder(); } diff --git a/aws-sns/build.gradle b/aws-sns/build.gradle index 04a96fdf0..7f645aa48 100644 --- a/aws-sns/build.gradle +++ b/aws-sns/build.gradle @@ -2,8 +2,9 @@ description = "AWS SNS" dependencies { - api 'software.amazon.awssdk:sns:2.19.2' - implementation group: 'software.amazon.awssdk', name: 'url-connection-client', version: '2.19.2' + api 'software.amazon.awssdk:sns:2.20.33' + implementation group: 'software.amazon.awssdk', name: 'url-connection-client', version: '2.20.33' + implementation project(':aws-xray') configurations.all { exclude group: 'software.amazon.awssdk', module: 'apache-client' diff --git a/aws-sns/config/checkstyle/import-control.xml b/aws-sns/config/checkstyle/import-control.xml index fadd8e6ff..5ce8fc54a 100644 --- a/aws-sns/config/checkstyle/import-control.xml +++ b/aws-sns/config/checkstyle/import-control.xml @@ -15,6 +15,9 @@ + + + diff --git a/aws-sns/src/main/java/com/formkiq/aws/sns/SnsConnectionBuilder.java b/aws-sns/src/main/java/com/formkiq/aws/sns/SnsConnectionBuilder.java index 0d47c2833..407284382 100644 --- a/aws-sns/src/main/java/com/formkiq/aws/sns/SnsConnectionBuilder.java +++ b/aws-sns/src/main/java/com/formkiq/aws/sns/SnsConnectionBuilder.java @@ -24,9 +24,12 @@ package com.formkiq.aws.sns; import java.net.URI; +import com.amazonaws.xray.interceptors.TracingInterceptor; import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider; import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration.Builder; import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.sns.SnsClient; @@ -46,12 +49,21 @@ public class SnsConnectionBuilder { /** * constructor. + * + * @param enableAwsXray Enable AWS X-Ray */ - public SnsConnectionBuilder() { + public SnsConnectionBuilder(final boolean enableAwsXray) { System.setProperty("software.amazon.awssdk.http.service.impl", "software.amazon.awssdk.http.urlconnection.UrlConnectionSdkHttpService"); - this.builder = SnsClient.builder().httpClientBuilder(UrlConnectionHttpClient.builder()) + Builder clientConfig = ClientOverrideConfiguration.builder(); + + if (enableAwsXray) { + clientConfig.addExecutionInterceptor(new TracingInterceptor()); + } + + this.builder = SnsClient.builder().overrideConfiguration(clientConfig.build()) + .httpClientBuilder(UrlConnectionHttpClient.builder()) .credentialsProvider(EnvironmentVariableCredentialsProvider.create()); } diff --git a/aws-sqs/build.gradle b/aws-sqs/build.gradle index b70284e8e..57061b03a 100644 --- a/aws-sqs/build.gradle +++ b/aws-sqs/build.gradle @@ -2,8 +2,15 @@ description = "AWS SQS" dependencies { - api 'software.amazon.awssdk:sqs:2.19.2' - implementation group: 'software.amazon.awssdk', name: 'url-connection-client', version: '2.19.2' + + annotationProcessor group: 'com.formkiq', name: 'graalvm-annotations-processor', version: '1.4.0' + + implementation group: 'com.formkiq', name: 'graalvm-annotations', version: '1.2.0' + implementation group: 'com.google.code.gson', name: 'gson', version: '2.10.1' + + api 'software.amazon.awssdk:sqs:2.20.33' + implementation group: 'software.amazon.awssdk', name: 'url-connection-client', version: '2.20.33' + implementation project(':aws-xray') implementation project(':fkq-lambda-services') configurations.all { @@ -11,8 +18,9 @@ dependencies { exclude group: 'software.amazon.awssdk', module: 'netty-nio-client' } - testImplementation group: 'junit', name: 'junit', version:'4.+' + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version:'5.9.1' } test { + useJUnitPlatform() } \ No newline at end of file diff --git a/aws-sqs/config/checkstyle/import-control.xml b/aws-sqs/config/checkstyle/import-control.xml index f9738c353..84df9e3a2 100644 --- a/aws-sqs/config/checkstyle/import-control.xml +++ b/aws-sqs/config/checkstyle/import-control.xml @@ -15,6 +15,11 @@ + + + + + diff --git a/aws-sqs/src/main/java/com/formkiq/aws/sqs/SqsConnectionBuilder.java b/aws-sqs/src/main/java/com/formkiq/aws/sqs/SqsConnectionBuilder.java index 0e378f65a..c7608fac0 100644 --- a/aws-sqs/src/main/java/com/formkiq/aws/sqs/SqsConnectionBuilder.java +++ b/aws-sqs/src/main/java/com/formkiq/aws/sqs/SqsConnectionBuilder.java @@ -24,9 +24,12 @@ package com.formkiq.aws.sqs; import java.net.URI; +import com.amazonaws.xray.interceptors.TracingInterceptor; import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider; import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration.Builder; import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.sqs.SqsClient; @@ -46,12 +49,21 @@ public class SqsConnectionBuilder { /** * constructor. + * + * @param enableAwsXray Enable Aws X-Ray */ - public SqsConnectionBuilder() { + public SqsConnectionBuilder(final boolean enableAwsXray) { System.setProperty("software.amazon.awssdk.http.service.impl", "software.amazon.awssdk.http.urlconnection.UrlConnectionSdkHttpService"); - this.builder = SqsClient.builder().httpClientBuilder(UrlConnectionHttpClient.builder()) + Builder clientConfig = ClientOverrideConfiguration.builder(); + + if (enableAwsXray) { + clientConfig.addExecutionInterceptor(new TracingInterceptor()); + } + + this.builder = SqsClient.builder().overrideConfiguration(clientConfig.build()) + .httpClientBuilder(UrlConnectionHttpClient.builder()) .credentialsProvider(EnvironmentVariableCredentialsProvider.create()); } diff --git a/aws-sqs/src/main/java/com/formkiq/aws/sqs/SqsMessageRecord.java b/aws-sqs/src/main/java/com/formkiq/aws/sqs/SqsMessageRecord.java new file mode 100644 index 000000000..3e7aa0cbb --- /dev/null +++ b/aws-sqs/src/main/java/com/formkiq/aws/sqs/SqsMessageRecord.java @@ -0,0 +1,217 @@ +/** + * MIT License + * + * Copyright (c) 2018 - 2020 FormKiQ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.formkiq.aws.sqs; + +import java.util.Map; +import com.formkiq.graalvm.annotations.Reflectable; + +/** + * Sqs Message Record Object. + * + */ +@Reflectable +public class SqsMessageRecord { + + /** Record Attributes. */ + private Map attributes; + /** AWS Region. */ + private String awsRegion; + /** Request body. */ + private String body; + /** Event Source. */ + private String eventSource; + /** Md5 of body. */ + private String md5OfBody; + /** Message Attributes. */ + private Map messageAttributes; + /** MessageId. */ + private String messageId; + /** Receipt Handle. */ + private String receiptHandle; + + /** + * constructor. + */ + public SqsMessageRecord() {} + + /** + * Get Attributes. + * + * @return {@link Map} + */ + public Map attributes() { + return this.attributes; + } + + /** + * Set Attributes. + * + * @param map {@link Map} + * @return {@link SqsMessageRecord} + */ + public SqsMessageRecord attributes(final Map map) { + this.attributes = map; + return this; + } + + /** + * Get AWS Region. + * + * @return {@link String} + */ + public String awsRegion() { + return this.awsRegion; + } + + /** + * Set AWS Region. + * + * @param region {@link String} + * @return {@link SqsMessageRecord} + */ + public SqsMessageRecord awsRegion(final String region) { + this.awsRegion = region; + return this; + } + + /** + * Get Body. + * + * @return {@link String} + */ + public String body() { + return this.body; + } + + /** + * Set Body. + * + * @param s {@link String} + * @return {@link SqsMessageRecord} + */ + public SqsMessageRecord body(final String s) { + this.body = s; + return this; + } + + /** + * Get Event Source. + * + * @return {@link String} + */ + public String eventSource() { + return this.eventSource; + } + + /** + * Set Event Source. + * + * @param source {@link String} + * @return {@link SqsMessageRecord} + */ + public SqsMessageRecord eventSource(final String source) { + this.eventSource = source; + return this; + } + + /** + * Get Md5 Hash of Body. + * + * @return {@link String} + */ + public String md5OfBody() { + return this.md5OfBody; + } + + /** + * Set Md5 Hash. + * + * @param md5 {@link String} + * @return {@link SqsMessageRecord} + */ + public SqsMessageRecord md5OfBody(final String md5) { + this.md5OfBody = md5; + return this; + } + + /** + * Get Message Attributes. + * + * @return {@link Map} + */ + public Map messageAttributes() { + return this.messageAttributes; + } + + /** + * Set Message Attributes. + * + * @param map {@link Map} + * @return {@link SqsMessageRecord} + */ + public SqsMessageRecord messageAttributes(final Map map) { + this.messageAttributes = map; + return this; + } + + /** + * Get Message Id. + * + * @return {@link String} + */ + public String messageId() { + return this.messageId; + } + + /** + * Set Message Id. + * + * @param id {@link String} + * @return {@link SqsMessageRecord} + */ + public SqsMessageRecord messageId(final String id) { + this.messageId = id; + return this; + } + + /** + * Get Receipt Handle. + * + * @return {@link String} + */ + public String receiptHandle() { + return this.receiptHandle; + } + + /** + * Set Receipt Handle. + * + * @param receipt {@link String} + * @return {@link SqsMessageRecord} + */ + public SqsMessageRecord receiptHandle(final String receipt) { + this.receiptHandle = receipt; + return this; + } +} diff --git a/aws-sqs/src/main/java/com/formkiq/aws/sqs/SqsMessageRecords.java b/aws-sqs/src/main/java/com/formkiq/aws/sqs/SqsMessageRecords.java new file mode 100644 index 000000000..969a2fa0d --- /dev/null +++ b/aws-sqs/src/main/java/com/formkiq/aws/sqs/SqsMessageRecords.java @@ -0,0 +1,68 @@ +/** + * MIT License + * + * Copyright (c) 2018 - 2020 FormKiQ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.formkiq.aws.sqs; + +import java.util.ArrayList; +import java.util.List; +import com.formkiq.graalvm.annotations.Reflectable; +import com.google.gson.annotations.SerializedName; + +/** + * Sqs Records Object. + * + */ +@Reflectable +public class SqsMessageRecords { + + /** Input Records. */ + @SerializedName("Records") + private List records; + + /** + * constructor. + */ + public SqsMessageRecords() { + this.records = new ArrayList<>(); + } + + /** + * Get Records. + * + * @return {@link List} {@link SqsMessageRecord} + */ + public List records() { + return this.records; + } + + /** + * Set Records. + * + * @param list {@link List} {@link SqsMessageRecord} + * @return {@link SqsMessageRecords} + */ + public SqsMessageRecords records(final List list) { + this.records = list; + return this; + } +} diff --git a/aws-sqs/src/test/java/com/formkiq/aws/sqs/SqsServiceTest.java b/aws-sqs/src/test/java/com/formkiq/aws/sqs/SqsServiceTest.java index 63bc88955..b7664e75b 100644 --- a/aws-sqs/src/test/java/com/formkiq/aws/sqs/SqsServiceTest.java +++ b/aws-sqs/src/test/java/com/formkiq/aws/sqs/SqsServiceTest.java @@ -23,8 +23,8 @@ */ package com.formkiq.aws.sqs; -import static org.junit.Assert.assertEquals; -import org.junit.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Test; /** * diff --git a/aws-ssm/build.gradle b/aws-ssm/build.gradle index 79892c13e..006ec5011 100644 --- a/aws-ssm/build.gradle +++ b/aws-ssm/build.gradle @@ -2,8 +2,9 @@ description = "AWS SSM" dependencies { - api group: 'software.amazon.awssdk', name: 'ssm', version: '2.19.2' - implementation group: 'software.amazon.awssdk', name: 'url-connection-client', version: '2.19.2' + api group: 'software.amazon.awssdk', name: 'ssm', version: '2.20.33' + implementation group: 'software.amazon.awssdk', name: 'url-connection-client', version: '2.20.33' + implementation project(':aws-xray') implementation project(':fkq-lambda-services') configurations.all { @@ -11,11 +12,12 @@ dependencies { exclude group: 'software.amazon.awssdk', module: 'netty-nio-client' } - testImplementation group: 'org.slf4j', name: 'slf4j-simple', version: '2.0.6' - testImplementation group: 'junit', name: 'junit', version:'4.+' + testImplementation group: 'org.slf4j', name: 'slf4j-simple', version: '2.0.7' + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version:'5.9.1' testImplementation group: 'org.testcontainers', name: 'localstack', version: '1.17.6' - testImplementation group: 'com.amazonaws', name: 'aws-java-sdk', version: '1.12.370' + testImplementation group: 'com.amazonaws', name: 'aws-java-sdk', version: '1.12.436' } test { + useJUnitPlatform() } \ No newline at end of file diff --git a/aws-ssm/config/checkstyle/import-control.xml b/aws-ssm/config/checkstyle/import-control.xml index 075c7baf9..8aa6cbc5f 100644 --- a/aws-ssm/config/checkstyle/import-control.xml +++ b/aws-ssm/config/checkstyle/import-control.xml @@ -15,6 +15,8 @@ + + diff --git a/aws-ssm/src/main/java/com/formkiq/aws/ssm/SsmConnectionBuilder.java b/aws-ssm/src/main/java/com/formkiq/aws/ssm/SsmConnectionBuilder.java index 323dc5913..8b63f212b 100644 --- a/aws-ssm/src/main/java/com/formkiq/aws/ssm/SsmConnectionBuilder.java +++ b/aws-ssm/src/main/java/com/formkiq/aws/ssm/SsmConnectionBuilder.java @@ -24,9 +24,12 @@ package com.formkiq.aws.ssm; import java.net.URI; +import com.amazonaws.xray.interceptors.TracingInterceptor; import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider; import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration.Builder; import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.ssm.SsmClient; @@ -46,12 +49,21 @@ public class SsmConnectionBuilder { /** * constructor. + * + * @param enableAwsXray Enable Aws X-Ray */ - public SsmConnectionBuilder() { + public SsmConnectionBuilder(final boolean enableAwsXray) { System.setProperty("software.amazon.awssdk.http.service.impl", "software.amazon.awssdk.http.urlconnection.UrlConnectionSdkHttpService"); - this.builder = SsmClient.builder().httpClientBuilder(UrlConnectionHttpClient.builder()) + Builder clientConfig = ClientOverrideConfiguration.builder(); + + if (enableAwsXray) { + clientConfig.addExecutionInterceptor(new TracingInterceptor()); + } + + this.builder = SsmClient.builder().overrideConfiguration(clientConfig.build()) + .httpClientBuilder(UrlConnectionHttpClient.builder()) .credentialsProvider(EnvironmentVariableCredentialsProvider.create()); } diff --git a/aws-ssm/src/test/java/com/formkiq/aws/ssm/SsmServiceCacheTest.java b/aws-ssm/src/test/java/com/formkiq/aws/ssm/SsmServiceCacheTest.java index 8dacf8c52..68bf103cd 100644 --- a/aws-ssm/src/test/java/com/formkiq/aws/ssm/SsmServiceCacheTest.java +++ b/aws-ssm/src/test/java/com/formkiq/aws/ssm/SsmServiceCacheTest.java @@ -23,17 +23,17 @@ */ package com.formkiq.aws.ssm; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.UUID; import java.util.concurrent.TimeUnit; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.testcontainers.containers.localstack.LocalStackContainer; import org.testcontainers.containers.localstack.LocalStackContainer.Service; import org.testcontainers.utility.DockerImageName; @@ -67,7 +67,7 @@ public class SsmServiceCacheTest { * @throws URISyntaxException URISyntaxException * @throws InterruptedException InterruptedException */ - @BeforeClass + @BeforeAll public static void beforeClass() throws IOException, URISyntaxException, InterruptedException { AwsCredentialsProvider cred = StaticCredentialsProvider @@ -76,7 +76,7 @@ public static void beforeClass() throws IOException, URISyntaxException, Interru localstack.start(); SsmConnectionBuilder connection = - new SsmConnectionBuilder().setCredentials(cred).setRegion(Region.US_EAST_1) + new SsmConnectionBuilder(false).setCredentials(cred).setRegion(Region.US_EAST_1) .setEndpointOverride(new URI(localstack.getEndpointOverride(Service.SSM).toString())); cache = new SsmServiceCache(connection, 1, TimeUnit.SECONDS); @@ -85,7 +85,7 @@ public static void beforeClass() throws IOException, URISyntaxException, Interru /** * AfterClass. */ - @AfterClass + @AfterAll public static void afterClass() { localstack.stop(); } diff --git a/aws-sts/build.gradle b/aws-sts/build.gradle index 534d52575..39ce2884a 100644 --- a/aws-sts/build.gradle +++ b/aws-sts/build.gradle @@ -2,11 +2,11 @@ description = "AWS STS" dependencies { - api group: 'software.amazon.awssdk', name: 'sts', version: '2.19.2' - implementation group: 'software.amazon.awssdk', name: 'url-connection-client', version: '2.19.2' + api group: 'software.amazon.awssdk', name: 'sts', version: '2.20.33' + implementation group: 'software.amazon.awssdk', name: 'url-connection-client', version: '2.20.33' implementation group: 'commons-codec', name: 'commons-codec', version: '1.15' - testImplementation group: 'junit', name: 'junit', version:'4.+' + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version:'5.9.1' configurations.all { exclude group: 'software.amazon.awssdk', module: 'apache-client' diff --git a/aws-xray/.checkstyle b/aws-xray/.checkstyle new file mode 100644 index 000000000..6fe039afd --- /dev/null +++ b/aws-xray/.checkstyle @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/aws-xray/.classpath b/aws-xray/.classpath new file mode 100644 index 000000000..66f8050c0 --- /dev/null +++ b/aws-xray/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/aws-xray/.gitignore b/aws-xray/.gitignore new file mode 100644 index 000000000..35fb74893 --- /dev/null +++ b/aws-xray/.gitignore @@ -0,0 +1,3 @@ +/bin/ +/build/ +/.gradle/ diff --git a/aws-xray/.project b/aws-xray/.project new file mode 100644 index 000000000..0d05173d0 --- /dev/null +++ b/aws-xray/.project @@ -0,0 +1,30 @@ + + + aws-xray + Project core created by Buildship. + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.buildship.core.gradleprojectbuilder + + + + + net.sf.eclipsecs.core.CheckstyleBuilder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.buildship.core.gradleprojectnature + net.sf.eclipsecs.core.CheckstyleNature + + + diff --git a/aws-xray/.settings/org.eclipse.buildship.core.prefs b/aws-xray/.settings/org.eclipse.buildship.core.prefs new file mode 100644 index 000000000..e8895216f --- /dev/null +++ b/aws-xray/.settings/org.eclipse.buildship.core.prefs @@ -0,0 +1,2 @@ +connection.project.dir= +eclipse.preferences.version=1 diff --git a/aws-xray/.settings/org.eclipse.core.resources.prefs b/aws-xray/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 000000000..99f26c020 --- /dev/null +++ b/aws-xray/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/aws-xray/.settings/org.eclipse.jdt.core.prefs b/aws-xray/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..9ba0f3224 --- /dev/null +++ b/aws-xray/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,496 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.codeComplete.argumentPrefixes= +org.eclipse.jdt.core.codeComplete.argumentSuffixes= +org.eclipse.jdt.core.codeComplete.fieldPrefixes= +org.eclipse.jdt.core.codeComplete.fieldSuffixes= +org.eclipse.jdt.core.codeComplete.localPrefixes= +org.eclipse.jdt.core.codeComplete.localSuffixes= +org.eclipse.jdt.core.codeComplete.staticFieldPrefixes= +org.eclipse.jdt.core.codeComplete.staticFieldSuffixes= +org.eclipse.jdt.core.codeComplete.staticFinalFieldPrefixes= +org.eclipse.jdt.core.codeComplete.staticFinalFieldSuffixes= +org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled +org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore +org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull +org.eclipse.jdt.core.compiler.annotation.nonnull.secondary= +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary= +org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable +org.eclipse.jdt.core.compiler.annotation.nullable.secondary= +org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=11 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=11 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.doc.comment.support=enabled +org.eclipse.jdt.core.compiler.problem.APILeak=warning +org.eclipse.jdt.core.compiler.problem.annotatedTypeArgumentToUnannotated=info +org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.autoboxing=warning +org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning +org.eclipse.jdt.core.compiler.problem.deadCode=warning +org.eclipse.jdt.core.compiler.problem.deprecation=warning +org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled +org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled +org.eclipse.jdt.core.compiler.problem.discouragedReference=warning +org.eclipse.jdt.core.compiler.problem.emptyStatement=warning +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore +org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=warning +org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning +org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning +org.eclipse.jdt.core.compiler.problem.forbiddenReference=error +org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning +org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled +org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning +org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning +org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=warning +org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning +org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=disabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=disabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=public +org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled +org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning +org.eclipse.jdt.core.compiler.problem.missingJavadocComments=warning +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public +org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag +org.eclipse.jdt.core.compiler.problem.missingJavadocTags=warning +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=public +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning +org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore +org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning +org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning +org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore +org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning +org.eclipse.jdt.core.compiler.problem.nonnullTypeVariableFromLegacyInvocation=warning +org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error +org.eclipse.jdt.core.compiler.problem.nullReference=warning +org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error +org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=warning +org.eclipse.jdt.core.compiler.problem.pessimisticNullAnalysisForFreeTypeVariables=warning +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning +org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning +org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=warning +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning +org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning +org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.suppressWarningsNotFullyAnalysed=info +org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=warning +org.eclipse.jdt.core.compiler.problem.terminalDeprecation=warning +org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning +org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled +org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning +org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning +org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore +org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning +org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentType=warning +org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentTypeStrict=disabled +org.eclipse.jdt.core.compiler.problem.unlikelyEqualsArgumentType=info +org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=warning +org.eclipse.jdt.core.compiler.problem.unstableAutoModuleName=warning +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled +org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedImport=warning +org.eclipse.jdt.core.compiler.problem.unusedLabel=warning +org.eclipse.jdt.core.compiler.problem.unusedLocal=warning +org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=warning +org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled +org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning +org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.source=11 +org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns=false +org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647 +org.eclipse.jdt.core.formatter.align_type_members_on_columns=false +org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns=false +org.eclipse.jdt.core.formatter.align_with_spaces=false +org.eclipse.jdt.core.formatter.alignment_for_additive_operator=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_assignment=16 +org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_loops=16 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression_chain=0 +org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header=0 +org.eclipse.jdt.core.formatter.alignment_for_logical_operator=16 +org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_module_statements=16 +org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +org.eclipse.jdt.core.formatter.alignment_for_multiplicative_operator=16 +org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references=0 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_relational_operator=0 +org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80 +org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_shift_operator=0 +org.eclipse.jdt.core.formatter.alignment_for_string_concatenation=16 +org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_type_arguments=0 +org.eclipse.jdt.core.formatter.alignment_for_type_parameters=0 +org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16 +org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_after_package=1 +org.eclipse.jdt.core.formatter.blank_lines_before_field=0 +org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 +org.eclipse.jdt.core.formatter.blank_lines_before_imports=0 +org.eclipse.jdt.core.formatter.blank_lines_before_member_type=0 +org.eclipse.jdt.core.formatter.blank_lines_before_method=1 +org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 +org.eclipse.jdt.core.formatter.blank_lines_before_package=0 +org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=0 +org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=2 +org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.comment.align_tags_descriptions_grouped=false +org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions=false +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false +org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position=true +org.eclipse.jdt.core.formatter.comment.format_block_comments=true +org.eclipse.jdt.core.formatter.comment.format_header=true +org.eclipse.jdt.core.formatter.comment.format_html=true +org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true +org.eclipse.jdt.core.formatter.comment.format_line_comments=true +org.eclipse.jdt.core.formatter.comment.format_source_code=true +org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false +org.eclipse.jdt.core.formatter.comment.indent_root_tags=true +org.eclipse.jdt.core.formatter.comment.indent_tag_description=false +org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert +org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert +org.eclipse.jdt.core.formatter.comment.line_length=100 +org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true +org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true +org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false +org.eclipse.jdt.core.formatter.compact_else_if=true +org.eclipse.jdt.core.formatter.continuation_indentation=2 +org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2 +org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off +org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on +org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false +org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true +org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_empty_lines=false +org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true +org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true +org.eclipse.jdt.core.formatter.indentation.size=2 +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert +org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_after_additive_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_bitwise_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert +org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_after_logical_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_multiplicative_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_relational_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert +org.eclipse.jdt.core.formatter.insert_space_after_shift_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_string_concatenation=insert +org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_additive_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_bitwise_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_before_logical_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_multiplicative_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert +org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_relational_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_shift_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation=insert +org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.join_lines_in_comments=true +org.eclipse.jdt.core.formatter.join_wrapped_lines=true +org.eclipse.jdt.core.formatter.keep_annotation_declaration_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_anonymous_type_declaration_on_one_line=one_line_if_empty +org.eclipse.jdt.core.formatter.keep_code_block_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false +org.eclipse.jdt.core.formatter.keep_enum_constant_declaration_on_one_line=one_line_if_empty +org.eclipse.jdt.core.formatter.keep_enum_declaration_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_if_then_body_block_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false +org.eclipse.jdt.core.formatter.keep_lambda_body_block_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_loop_body_block_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_method_body_on_one_line=one_line_if_empty +org.eclipse.jdt.core.formatter.keep_simple_do_while_body_on_same_line=false +org.eclipse.jdt.core.formatter.keep_simple_for_body_on_same_line=false +org.eclipse.jdt.core.formatter.keep_simple_getter_setter_on_one_line=false +org.eclipse.jdt.core.formatter.keep_simple_while_body_on_same_line=false +org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_type_declaration_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.lineSplit=100 +org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false +org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 +org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=3 +org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause=common_lines +org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=false +org.eclipse.jdt.core.formatter.tabulation.char=space +org.eclipse.jdt.core.formatter.tabulation.size=2 +org.eclipse.jdt.core.formatter.use_on_off_tags=true +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false +org.eclipse.jdt.core.formatter.wrap_before_additive_operator=true +org.eclipse.jdt.core.formatter.wrap_before_assignment_operator=false +org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true +org.eclipse.jdt.core.formatter.wrap_before_bitwise_operator=true +org.eclipse.jdt.core.formatter.wrap_before_conditional_operator=true +org.eclipse.jdt.core.formatter.wrap_before_logical_operator=true +org.eclipse.jdt.core.formatter.wrap_before_multiplicative_operator=true +org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true +org.eclipse.jdt.core.formatter.wrap_before_relational_operator=true +org.eclipse.jdt.core.formatter.wrap_before_shift_operator=true +org.eclipse.jdt.core.formatter.wrap_before_string_concatenation=true +org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true +org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter diff --git a/aws-xray/.settings/org.eclipse.jdt.ui.prefs b/aws-xray/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 000000000..c13d929fd --- /dev/null +++ b/aws-xray/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,8 @@ +eclipse.preferences.version=1 +formatter_profile=_GoogleStyle +formatter_settings_version=16 +org.eclipse.jdt.ui.exception.name=e +org.eclipse.jdt.ui.gettersetter.use.is=true +org.eclipse.jdt.ui.keywordthis=true +org.eclipse.jdt.ui.overrideannotation=true +org.eclipse.jdt.ui.text.custom_code_templates= diff --git a/aws-xray/build.gradle b/aws-xray/build.gradle new file mode 100644 index 000000000..fa8fbc5ba --- /dev/null +++ b/aws-xray/build.gradle @@ -0,0 +1,6 @@ + +description = "AWS X-Ray" + +dependencies { + api group: 'com.amazonaws', name: 'aws-xray-recorder-sdk-aws-sdk-v2', version: '2.14.0' +} diff --git a/aws-xray/config/checkstyle/checkstyle.xml b/aws-xray/config/checkstyle/checkstyle.xml new file mode 100644 index 000000000..9077493f9 --- /dev/null +++ b/aws-xray/config/checkstyle/checkstyle.xml @@ -0,0 +1,239 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/aws-xray/config/checkstyle/import-control.xml b/aws-xray/config/checkstyle/import-control.xml new file mode 100644 index 000000000..ea41a24ba --- /dev/null +++ b/aws-xray/config/checkstyle/import-control.xml @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/aws-xray/config/checkstyle/mysuppressions.xml b/aws-xray/config/checkstyle/mysuppressions.xml new file mode 100644 index 000000000..6e2ebd3fd --- /dev/null +++ b/aws-xray/config/checkstyle/mysuppressions.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/aws-xray/src/main/java/empty b/aws-xray/src/main/java/empty new file mode 100644 index 000000000..e69de29bb diff --git a/aws-xray/src/main/resources/META-INF/native-image/com.formkiq/aws-xray/reflect-config.json b/aws-xray/src/main/resources/META-INF/native-image/com.formkiq/aws-xray/reflect-config.json new file mode 100644 index 000000000..ed1b72537 --- /dev/null +++ b/aws-xray/src/main/resources/META-INF/native-image/com.formkiq/aws-xray/reflect-config.json @@ -0,0 +1,33 @@ +[ + { + "name": "org.apache.commons.logging.impl.LogFactoryImpl", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "name": "org.apache.commons.logging.impl.Jdk14Logger", + "methods": [ + { + "name": "", + "parameterTypes": [ + "java.lang.String" + ] + } + ] + }, + { + "name": "java.util.HashSet", + "queryAllDeclaredMethods": true, + "queryAllDeclaredConstructors": true, + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + } +] \ No newline at end of file diff --git a/aws-xray/src/test/java/empty b/aws-xray/src/test/java/empty new file mode 100644 index 000000000..e69de29bb diff --git a/aws-xray/src/test/resources/empty b/aws-xray/src/test/resources/empty new file mode 100644 index 000000000..e69de29bb diff --git a/build.gradle b/build.gradle index 82d880eaf..d90974e82 100644 --- a/build.gradle +++ b/build.gradle @@ -2,20 +2,30 @@ plugins { id 'java-library' id 'checkstyle' - id 'com.github.spotbugs' version '5.0.13' - id 'com.diffplug.spotless' version '6.12.0' - id 'com.github.ben-manes.versions' version '0.42.0' - id 'com.formkiq.gradle.graalvm-native-plugin' version '1.4.0' - id 'de.undercouch.download' version '5.3.0' + id 'com.github.spotbugs' version '5.0.14' + id 'com.diffplug.spotless' version '6.17.0' + id 'com.github.ben-manes.versions' version '0.46.0' + id 'com.formkiq.gradle.graalvm-native-plugin' version '1.5.0' + id 'de.undercouch.download' version '5.4.0' } def moduleName = "formkiq-core" +def getCmd() { + String os = System.getProperty("os.name").toLowerCase() + return os.contains("win") ? "cmd" : "bash" +} + +def getCmdParam() { + String os = System.getProperty("os.name").toLowerCase() + return os.contains("win") ? "/c" : "-c" +} + repositories { mavenCentral() } allprojects { - version = '1.10.1' - ext.awsCognitoVersion = '1.4.5' + version = '1.11.0' + ext.awsCognitoVersion = '1.5.0' group = 'com.formkiq.stacks' apply plugin: 'com.diffplug.spotless' @@ -34,7 +44,6 @@ allprojects { } spotlessJavaCheck.dependsOn 'spotlessJavaApply' - } subprojects { @@ -62,6 +71,7 @@ subprojects { systemProperties['testregion'] = project.getProperty('testregion') systemProperties['testprofile'] = project.getProperty('testprofile') systemProperties['testappenvironment'] = project.getProperty('testappenvironment') + systemProperties['testchatgptapikey'] = project.getProperty('testchatgptapikey') } spotbugs { @@ -104,22 +114,19 @@ subprojects { description = "FormKiQ Core" -task testaws { - dependsOn subprojects.collect { subproject -> - subproject.tasks.matching { it.name == "testaws" } - } -} - task buildDistribution(type: Copy) { dependsOn subprojects.build + duplicatesStrategy = DuplicatesStrategy.EXCLUDE outputs.upToDateWhen {false} from 'console/build/distributions/formkiq-core' from 'lambda-api/build/distributions/formkiq-core' from 'lambda-s3/build/distributions/formkiq-core' from 'lambda-typesense/build/distributions/formkiq-module-lambda-typesense' + from 'lambda-ocr-tesseract/build/distributions/formkiq-core' from 'module-email-notify/build/distributions/formkiq-core' from 'websocket-api/build/distributions/formkiq-core' + from 'lambda-apikey-authorizer/build/distributions/formkiq-core' into "${buildDir}/distributions/formkiq-core" } @@ -127,167 +134,19 @@ task assembleTemplate { dependsOn buildDistribution outputs.upToDateWhen { false } doLast { + copy { + from layout.buildDirectory.dir("${buildDir}/../src/main/resources/cloudformation") + include "*" + into "${buildDir}/distributions/formkiq-core/sam" + } exec { - commandLine "bash", "-c", "cp src/main/resources/cloudformation/* ${buildDir}/distributions/formkiq-core/sam/" - } - exec { - commandLine "bash", "-c", "ytt --data-value version=${project.version} -f ${buildDir}/distributions/formkiq-core/sam/template.yaml --output-files ${buildDir}/distributions/formkiq-core/sam" - } - exec { - commandLine "bash", "-c", "cp src/main/resources/cloudformation/module-*.yaml ${buildDir}/distributions/formkiq-core/sam/" + commandLine getCmd(), getCmdParam(), "ytt --data-value version=${project.version} -f ${buildDir}/distributions/formkiq-core/sam/template.yaml --output-files ${buildDir}/distributions/formkiq-core/sam" } } } check.dependsOn ':buildSamZip' -/** - * SAM Packges Uploads enterprise build to the customer's bucket. - */ -task uploadToCustomerBucket { - dependsOn build - outputs.upToDateWhen { false } - doLast { - exec { - commandLine "bash", "-c", "sam package --s3-prefix ${project.version} --s3-bucket ${moduleName} --template-file ${buildDir}/distributions/${moduleName}/sam/template.yaml --region ${testregion} --output-template-file ${buildDir}/distributions/${moduleName}/sam/template-${project.version}.yaml --profile ${testprofile}" - } - exec { - commandLine "bash", "-c", "aws s3 cp ${buildDir}/distributions/${moduleName}/sam/template-${project.version}.yaml s3://${moduleName}/${project.version}/template.yaml --only-show-errors --profile ${testprofile}" - } - } -} - -task deploy { - dependsOn assembleTemplate - outputs.upToDateWhen { false } - doLast { - exec { - commandLine "bash", "-c", "sam deploy --resolve-s3 --template-file ${buildDir}/distributions/formkiq-core/sam/template.yaml --stack-name formkiq-core-${testappenvironment} --parameter-overrides ParameterKey=AppEnvironment,ParameterValue=${testappenvironment} ParameterKey=AdminEmail,ParameterValue=${testadminemail} ParameterKey=EnablePublicUrls,ParameterValue=true --capabilities CAPABILITY_IAM CAPABILITY_AUTO_EXPAND CAPABILITY_NAMED_IAM --region ${testregion} --profile ${testprofile}" - } - } -} - -task undeploy { - outputs.upToDateWhen { false } - doLast { - def group = new ByteArrayOutputStream() - - // find ApiGatewayInvokeGroup Name - exec { - commandLine "bash", "-c", "aws iam list-groups --profile ${testprofile} | jq -r '.Groups[].GroupName' | grep '^formkiq-core-${testappenvironment}'" - standardOutput = group - ignoreExitValue = true - } - // remove any temp queues. - exec { - commandLine "bash", "-c", "aws sqs list-queues --region ${testregion} --profile ${testprofile} | jq -r '.QueueUrls[]' | grep '/createtest' | xargs -I{} aws sqs delete-queue --queue-url {} --region ${testregion} --profile ${testprofile}" - ignoreExitValue = true - } - // remove all users in ApiGatewayInvokeGroup - exec { - commandLine "bash", "-c", "aws iam get-group --group-name ${group.toString().trim()} --profile ${testprofile} | jq -r '.Users[].UserName' | xargs -I{} aws iam remove-user-from-group --group-name ${group.toString().trim()} --user-name {} --region ${testregion} --profile ${testprofile}" - ignoreExitValue = true - } - exec { - commandLine "bash", "-c", "aws sts get-caller-identity --profile ${testprofile} | jq -r '.Account' | xargs -I{} aws s3api put-bucket-versioning --bucket formkiq-core-${testappenvironment}-staging-{} --versioning-configuration Status=Suspended --region ${testregion} --profile ${testprofile}" - ignoreExitValue = true - } - exec { - commandLine "bash", "-c", "aws sts get-caller-identity --profile ${testprofile} | jq -r '.Account' | xargs -I{} aws s3api put-bucket-versioning --bucket formkiq-core-${testappenvironment}-documents-{} --versioning-configuration Status=Suspended --region ${testregion} --profile ${testprofile}" - ignoreExitValue = true - } - - // empty buckets - exec { - commandLine "bash", "-c", "aws sts get-caller-identity --profile ${testprofile} | jq -r '.Account' | xargs -I{} python3 ${project.projectDir}/emptyVersionedBucket.py -b formkiq-core-${testappenvironment}-console-{} -p ${testprofile}" - ignoreExitValue = true - } - exec { - commandLine "bash", "-c", "aws sts get-caller-identity --profile ${testprofile} | jq -r '.Account' | xargs -I{} python3 ${project.projectDir}/emptyVersionedBucket.py -b formkiq-core-${testappenvironment}-staging-{} -p ${testprofile}" - ignoreExitValue = true - } - exec { - commandLine "bash", "-c", "aws sts get-caller-identity --profile ${testprofile} | jq -r '.Account' | xargs -I{} python3 ${project.projectDir}/emptyVersionedBucket.py -b formkiq-core-${testappenvironment}-documents-{} -p ${testprofile}" - ignoreExitValue = true - } - - // delete buckets - exec { - commandLine "bash", "-c", "aws sts get-caller-identity --profile ${testprofile} | jq -r '.Account' | xargs -I{} aws s3 rb s3://formkiq-core-${testappenvironment}-console-{} --force --region ${testregion} --profile ${testprofile}" - ignoreExitValue = true - } - exec { - commandLine "bash", "-c", "aws sts get-caller-identity --profile ${testprofile} | jq -r '.Account' | xargs -I{} aws s3 rb s3://formkiq-core-${testappenvironment}-staging-{} --force --region ${testregion} --profile ${testprofile}" - ignoreExitValue = true - } - exec { - commandLine "bash", "-c", "aws sts get-caller-identity --profile ${testprofile} | jq -r '.Account' | xargs -I{} aws s3 rb s3://formkiq-core-${testappenvironment}-documents-{} --force --region ${testregion} --profile ${testprofile}" - ignoreExitValue = true - } - // sam delete - exec { - commandLine "bash", "-c", "sam delete --stack-name formkiq-core-${testappenvironment} --no-prompts --region ${testregion} --profile ${testprofile}" - } - // delete cloudwatch logs - exec { - commandLine "bash", "-c", "aws logs describe-log-groups --log-group-name-prefix /aws/lambda/formkiq-core-${testappenvironment} --region ${testregion} --profile ${testprofile} | jq -r '.logGroups[].logGroupName' | xargs -I{} aws logs delete-log-group --log-group-name {} --region ${testregion} --profile ${testprofile}" - } - /* - exec { - commandLine "bash", "-c", "aws cloudformation delete-stack --stack-name formkiq-core-${testappenvironment} --region ${testregion} --profile ${testprofile}" - } - // delete dynamodb tables - exec { - commandLine "bash", "-c", "aws dynamodb list-tables --region ${testregion} --profile ${testprofile} | jq -r '.TableNames[]' | grep '^formkiq-core-${testappenvironment}' | xargs -I {} aws dynamodb delete-table --table-name {} --region ${testregion} --profile ${testprofile}" - } - // disable s3 bucket versioning - exec { - commandLine "bash", "-c", "aws s3api list-buckets --query \"Buckets[].Name\" --profile ${testprofile} | jq -r '.[]' | grep '^formkiq-core-${testappenvironment}' | xargs -I{} aws s3api put-bucket-versioning --bucket {} --versioning-configuration Status=Suspended --region ${testregion} --profile ${testprofile}" - } - // delete all s3 objects - exec { - commandLine "bash", "-c", "aws s3api list-buckets --query \"Buckets[].Name\" --profile ${testprofile} | jq -r '.[]' | grep '^formkiq-core-${testappenvironment}' | xargs -I {} python3 ${project.projectDir}/emptyVersionedBucket.py -b {} -p ${testprofile}" - } - // delete s3 buckets - exec { - commandLine "bash", "-c", "aws s3api list-buckets --query \"Buckets[].Name\" --profile ${testprofile} | jq -r '.[]' | grep '^formkiq-core-${testappenvironment}' | xargs -I{} aws s3 rb s3://{} --force --region ${testregion} --profile ${testprofile}" - } - // delete cloudwatch logs - exec { - commandLine "bash", "-c", "aws logs describe-log-groups --log-group-name-prefix /aws/lambda/formkiq-core-${testappenvironment} --region ${testregion} --profile ${testprofile} | jq -r '.logGroups[].logGroupName' | xargs -I{} aws logs delete-log-group --log-group-name {} --region ${testregion} --profile ${testprofile}" - } - // wait for stack to be deleted - exec { - commandLine "bash", "-c", "aws cloudformation wait stack-delete-complete --stack-name formkiq-core-${testappenvironment} --region ${testregion} --profile ${testprofile}" - }*/ - } -} - -task createSar { - dependsOn build - outputs.upToDateWhen { false } - doLast { - exec { - commandLine "bash", "-c", "sam publish --template-file ${buildDir}/distributions/formkiq-core/sar/storage/template.yaml --semantic-version ${project.version} --region ${testregion} --profile ${testprofile}" - } - exec { - commandLine "bash", "-c", "sam publish --template-file ${buildDir}/distributions/formkiq-core/sar/api/template.yaml --semantic-version ${project.version} --region ${testregion} --profile ${testprofile}" - } - exec { - commandLine "bash", "-c", "sam publish --template-file ${buildDir}/distributions/formkiq-core/sar/console/template.yaml --semantic-version ${project.version} --region ${testregion} --profile ${testprofile}" - } - exec { - commandLine "bash", "-c", "sam publish --template-file ${buildDir}/distributions/formkiq-core/sar/module-email-notify/template.yaml --semantic-version ${project.version} --region ${testregion} --profile ${testprofile}" - } - exec { - commandLine "bash", "-c", "sam package --template-file ${buildDir}/distributions/formkiq-core/sar/template.yaml --resolve-s3 --region ${testregion} --profile ${testprofile} --output-template-file build/formkiq-core-sar.yaml" - } - exec { - commandLine "bash", "-c", "sam publish --template-file ${buildDir}/distributions/formkiq-core/sar/template.yaml --semantic-version ${project.version} --region ${testregion} --profile ${testprofile}" - } - } -} - task buildSamZip(type: Zip) { dependsOn assembleTemplate outputs.upToDateWhen { false } diff --git a/console/build.gradle b/console/build.gradle index 1e6d5e086..8d6ce1e62 100644 --- a/console/build.gradle +++ b/console/build.gradle @@ -1,28 +1,40 @@ description = "FormKiQ Core - Console" +sourceSets { + integration { + java.srcDir "$projectDir/src/integration/java" + resources.srcDir "$projectDir/src/integration/resources" + compileClasspath += main.output + test.output + runtimeClasspath += main.output + test.output + } +} + +configurations { + integrationImplementation.extendsFrom testImplementation + integrationRuntime.extendsFrom testRuntime +} + dependencies { implementation project(':aws-s3') implementation project(':aws-ssm') - implementation group: 'org.slf4j', name: 'slf4j-simple', version: '2.0.6' + implementation group: 'org.slf4j', name: 'slf4j-simple', version: '2.0.7' implementation group: 'com.amazonaws', name: 'aws-lambda-java-core', version: '1.2.2' implementation group: 'org.json', name: 'json', version: '20220924' testImplementation project(':aws-cognito') testImplementation project(':fkq-test-utils') - testImplementation group: 'com.google.code.gson', name: 'gson', version: '2.10' - testImplementation group: 'junit', name: 'junit', version:'4.+' - testImplementation group: 'org.slf4j', name: 'slf4j-simple', version: '2.0.6' + testImplementation group: 'com.google.code.gson', name: 'gson', version: '2.10.1' + testImplementation group: 'org.slf4j', name: 'slf4j-simple', version: '2.0.7' testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version:'5.9.1' - testImplementation group: 'org.junit.vintage', name: 'junit-vintage-engine', version:'5.9.1' testImplementation group: 'org.testcontainers', name: 'junit-jupiter', version: '1.17.6' testImplementation group: 'org.testcontainers', name: 'localstack', version: '1.17.6' - testImplementation group: 'com.amazonaws', name: 'aws-java-sdk', version: '1.12.370' - testImplementation group: 'com.microsoft.playwright', name: 'playwright', version: '1.28.1' + testImplementation group: 'com.amazonaws', name: 'aws-java-sdk', version: '1.12.436' + testImplementation group: 'com.microsoft.playwright', name: 'playwright', version: '1.32.0' } task buildZip(type: Zip) { @@ -47,14 +59,13 @@ task buildUnzip(type: Copy) { test { failFast = true - exclude 'com/formkiq/stacks/console/awstest/**' useJUnitPlatform() } -task testaws(type: Test) { - description = 'Runs AWS integration tests.' - include 'com/formkiq/stacks/console/awstest/**' - outputs.upToDateWhen {false} +task integrationTest(type: Test) { + testClassesDirs = sourceSets.integration.output.classesDirs + classpath = sourceSets.integration.runtimeClasspath + useJUnitPlatform() } task assembleTemplate { diff --git a/console/src/test/java/com/formkiq/stacks/console/awstest/AwsResourceTest.java b/console/src/integration/java/com/formkiq/stacks/console/awstest/AwsResourceTest.java similarity index 94% rename from console/src/test/java/com/formkiq/stacks/console/awstest/AwsResourceTest.java rename to console/src/integration/java/com/formkiq/stacks/console/awstest/AwsResourceTest.java index 6410d530e..59da33242 100644 --- a/console/src/test/java/com/formkiq/stacks/console/awstest/AwsResourceTest.java +++ b/console/src/integration/java/com/formkiq/stacks/console/awstest/AwsResourceTest.java @@ -23,8 +23,9 @@ */ package com.formkiq.stacks.console.awstest; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; @@ -33,8 +34,10 @@ import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import java.util.Map; -import org.junit.BeforeClass; -import org.junit.Test; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + import com.formkiq.aws.s3.S3ConnectionBuilder; import com.formkiq.aws.s3.S3ObjectMetadata; import com.formkiq.aws.s3.S3Service; @@ -48,6 +51,7 @@ import com.microsoft.playwright.Locator; import com.microsoft.playwright.Page; import com.microsoft.playwright.Playwright; + import software.amazon.awssdk.regions.Region; /** @@ -73,7 +77,7 @@ public class AwsResourceTest { * * @throws IOException IOException */ - @BeforeClass + @BeforeAll public static void beforeClass() throws IOException { Region region = Region.of(System.getProperty("testregion")); @@ -82,11 +86,11 @@ public static void beforeClass() throws IOException { appenvironment = System.getProperty("testappenvironment"); SsmConnectionBuilder ssmBuilder = - new SsmConnectionBuilder().setCredentials(awsprofile).setRegion(region); + new SsmConnectionBuilder(false).setCredentials(awsprofile).setRegion(region); ssmService = new SsmServiceImpl(ssmBuilder); final S3ConnectionBuilder s3Builder = - new S3ConnectionBuilder().setCredentials(awsprofile).setRegion(region); + new S3ConnectionBuilder(false).setCredentials(awsprofile).setRegion(region); s3 = new S3Service(s3Builder); FkqCognitoService cognito = new FkqCognitoService(awsprofile, region, appenvironment); diff --git a/console/src/main/java/com/formkiq/stacks/console/ConsoleInstallHandler.java b/console/src/main/java/com/formkiq/stacks/console/ConsoleInstallHandler.java index 7f837882f..b58afcf3f 100644 --- a/console/src/main/java/com/formkiq/stacks/console/ConsoleInstallHandler.java +++ b/console/src/main/java/com/formkiq/stacks/console/ConsoleInstallHandler.java @@ -59,8 +59,11 @@ public class ConsoleInstallHandler implements RequestHandler /** constructor. */ public ConsoleInstallHandler() { - this(System.getenv(), new S3ConnectionBuilder().setRegion(Region.US_EAST_1), - new S3ConnectionBuilder().setRegion(Region.of(System.getenv("REGION")))); + this(System.getenv(), + new S3ConnectionBuilder("true".equals(System.getenv("ENABLE_AWS_X_RAY"))) + .setRegion(Region.US_EAST_1), + new S3ConnectionBuilder("true".equals(System.getenv("ENABLE_AWS_X_RAY"))) + .setRegion(Region.of(System.getenv("REGION")))); } /** diff --git a/console/src/test/java/com/formkiq/stacks/console/ConsoleInstallHandlerTest.java b/console/src/test/java/com/formkiq/stacks/console/ConsoleInstallHandlerTest.java index 9721f4281..e45ac7248 100644 --- a/console/src/test/java/com/formkiq/stacks/console/ConsoleInstallHandlerTest.java +++ b/console/src/test/java/com/formkiq/stacks/console/ConsoleInstallHandlerTest.java @@ -23,8 +23,9 @@ */ package com.formkiq.stacks.console; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; @@ -33,15 +34,18 @@ import java.net.URL; import java.util.HashMap; import java.util.Map; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.testcontainers.containers.localstack.LocalStackContainer; import org.testcontainers.containers.localstack.LocalStackContainer.Service; import org.testcontainers.utility.DockerImageName; + import com.amazonaws.services.lambda.runtime.LambdaLogger; import com.formkiq.aws.s3.S3ConnectionBuilder; import com.formkiq.aws.s3.S3Service; + import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; import software.amazon.awssdk.auth.credentials.AwsSessionCredentials; import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; @@ -74,7 +78,7 @@ public class ConsoleInstallHandlerTest { * @throws InterruptedException InterruptedException * @throws URISyntaxException URISyntaxException */ - @BeforeClass + @BeforeAll public static void beforeClass() throws IOException, URISyntaxException, InterruptedException { AwsCredentialsProvider cred = StaticCredentialsProvider @@ -82,7 +86,7 @@ public static void beforeClass() throws IOException, URISyntaxException, Interru localStackInstance.start(); - s3Connection = new S3ConnectionBuilder().setCredentials(cred).setRegion(Region.US_EAST_1) + s3Connection = new S3ConnectionBuilder(false).setCredentials(cred).setRegion(Region.US_EAST_1) .setEndpointOverride( new URI(localStackInstance.getEndpointOverride(Service.S3).toString())); @@ -108,7 +112,7 @@ public static void beforeClass() throws IOException, URISyntaxException, Interru private LambdaLoggerRecorder logger = this.context.getLoggerRecorder(); /** before. */ - @Before + @BeforeEach public void before() { Map map = new HashMap<>(); diff --git a/docs/openapi/openapi-iam.yaml b/docs/openapi/openapi-iam.yaml index 65bab90bb..71a9b63e3 100644 --- a/docs/openapi/openapi-iam.yaml +++ b/docs/openapi/openapi-iam.yaml @@ -6,21 +6,21 @@ url: https://formkiq.com email: support@formkiq.com x-logo: - url: https://docs.formkiq.com/docs/1.9.0/_images/formkiq-logo.png + url: https://docs.formkiq.com/docs/latest/_images/formkiq-logo.png backgroundColor: '#FFFFFF' altText: FormKiQ Logo license: name: Apache 2.0 url: https://www.apache.org/licenses/LICENSE-2.0.html description: FormKiQ IAM API - version: 1.10.0 + version: 1.11.0 paths: /version: get: operationId: GetVersion description: Return the version of FormKiQ tags: - - Miscellaneous + - System Management responses: "200": description: 200 OK @@ -39,7 +39,7 @@ schema: $ref: '#/components/schemas/GetVersionRequest' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' /sites: @@ -47,7 +47,7 @@ operationId: GetSites description: Returns the list of sites that the user has access to tags: - - Miscellaneous + - System Management responses: "200": description: 200 OK @@ -66,7 +66,165 @@ schema: $ref: '#/components/schemas/GetSitesRequest' security: - - sigv4: [] + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + /configuration: + get: + operationId: GetConfigs + description: Returns the list of sites that the user has access to + tags: + - System Management + parameters: + - $ref: '#/components/parameters/siteIdParam' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + schema: + type: string + Access-Control-Allow-Methods: + schema: + type: string + Access-Control-Allow-Headers: + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/GetConfigurationResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + patch: + operationId: UpdateConfig + description: Update the System Management configuration + tags: + - System Management + parameters: + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SetConfigRequest' + examples: + Config: + value: + chatGptApiKey: ABC + maxContentLengthBytes: "1000000" + maxDocuments: "1000" + maxWebhooks: "10" + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/SetConfigResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + /configuration/apiKeys: + get: + operationId: GetApiKeys + description: Returns the list of ApiKeys + tags: + - System Management + parameters: + - $ref: '#/components/parameters/siteIdParam' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + schema: + type: string + Access-Control-Allow-Methods: + schema: + type: string + Access-Control-Allow-Headers: + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/GetApiKeysResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + post: + operationId: Ã…ddApiKey + description: Adds a new API Key + tags: + - System Management + parameters: + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AddApiKeyRequest' + examples: + AddApiKey: + value: + name: My API Key + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/AddApiKeyResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + /configuration/apiKeys/{apiKey}: + delete: + operationId: DeleteApiKey + description: Adds a new API Key + tags: + - System Management + parameters: + - $ref: '#/components/parameters/apiKeyParam' + - $ref: '#/components/parameters/siteIdParam' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/DeleteApiKeyResponse' + security: + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' /tagSchemas: @@ -106,7 +264,7 @@ schema: $ref: '#/components/schemas/GetTagSchemasRequest' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' post: @@ -150,7 +308,7 @@ schema: $ref: '#/components/schemas/ErrorsResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi201' /tagSchemas/{tagSchemaId}: @@ -177,7 +335,7 @@ schema: $ref: '#/components/schemas/GetTagSchemaRequest' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' delete: @@ -192,7 +350,7 @@ "200": $ref: '#/components/responses/200Cors' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' /documents: @@ -242,7 +400,7 @@ schema: $ref: '#/components/schemas/GetDocumentsResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' post: @@ -314,7 +472,7 @@ schema: $ref: '#/components/schemas/AddDocumentResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi201' /documents/{documentId}: @@ -341,7 +499,7 @@ schema: $ref: '#/components/schemas/GetDocumentResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' patch: @@ -390,7 +548,7 @@ schema: $ref: '#/components/schemas/AddDocumentResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' delete: @@ -405,7 +563,7 @@ "200": $ref: '#/components/responses/200Cors' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' /documents/{documentId}/versions: @@ -437,7 +595,7 @@ schema: $ref: '#/components/schemas/GetDocumentVersionsResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' put: @@ -475,7 +633,7 @@ schema: $ref: '#/components/schemas/ValidationErrorsResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' /documents/{documentId}/versions/{versionKey}: @@ -491,7 +649,7 @@ "200": $ref: '#/components/responses/200Cors' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' /documents/{documentId}/content: @@ -521,7 +679,7 @@ schema: $ref: '#/components/schemas/GetDocumentContentResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' /documents/{documentId}/tags: @@ -559,7 +717,7 @@ schema: $ref: '#/components/schemas/GetDocumentTagsResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' post: @@ -594,7 +752,7 @@ "201": $ref: '#/components/responses/200Cors' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi201' /documents/{documentId}/tags#: @@ -616,7 +774,7 @@ "201": $ref: '#/components/responses/200Cors' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi201' /documents/{documentId}/tags/{tagKey}: @@ -644,7 +802,7 @@ schema: $ref: '#/components/schemas/GetDocumentTagResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' put: @@ -666,7 +824,7 @@ "200": $ref: '#/components/responses/200Cors' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' delete: @@ -682,7 +840,7 @@ "200": $ref: '#/components/responses/200Cors' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' /documents/{documentId}/tags/{tagKey}/{tagValue}: @@ -700,7 +858,7 @@ "200": $ref: '#/components/responses/200Cors' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' /documents/{documentId}/url: @@ -735,7 +893,7 @@ schema: $ref: '#/components/schemas/GetDocumentUrlRequest' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' /documents/upload: @@ -768,7 +926,7 @@ schema: $ref: '#/components/schemas/GetDocumentUrlRequest' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' post: @@ -817,7 +975,7 @@ schema: $ref: '#/components/schemas/AddDocumentResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi201' /documents/{documentId}/upload: @@ -846,7 +1004,7 @@ schema: $ref: '#/components/schemas/GetDocumentUrlRequest' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' /search: @@ -916,7 +1074,7 @@ schema: $ref: '#/components/schemas/DocumentSearchResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/searchLambdaApi200' /searchFulltext: @@ -949,7 +1107,7 @@ schema: $ref: '#/components/schemas/DocumentFulltextResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200' /queryFulltext: @@ -981,7 +1139,7 @@ schema: $ref: '#/components/schemas/QueryFulltextResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200' /documents/{documentId}/actions: @@ -1008,7 +1166,7 @@ schema: $ref: '#/components/schemas/GetDocumentActionsResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' post: @@ -1040,7 +1198,7 @@ schema: $ref: '#/components/schemas/AddDocumentActionsResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' /documents/{documentId}/ocr: @@ -1069,7 +1227,7 @@ schema: $ref: '#/components/schemas/GetDocumentOcrResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/ocrLambdaApi200' post: @@ -1101,7 +1259,7 @@ schema: $ref: '#/components/schemas/AddDocumentOcrResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/ocrLambdaApi200' put: @@ -1133,7 +1291,7 @@ schema: $ref: '#/components/schemas/AddDocumentOcrResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/ocrLambdaApi200' delete: @@ -1148,7 +1306,7 @@ "200": $ref: '#/components/responses/200Cors' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/ocrLambdaApi200' /documents/{documentId}/antivirus: @@ -1181,7 +1339,7 @@ schema: $ref: '#/components/schemas/SetAntivirusResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/antivirusLambdaApi200' /documents/{documentId}/fulltext: @@ -1208,7 +1366,7 @@ schema: $ref: '#/components/schemas/GetDocumentFulltextResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200' delete: @@ -1223,7 +1381,7 @@ "200": $ref: '#/components/responses/200Cors' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200' put: @@ -1255,7 +1413,7 @@ schema: $ref: '#/components/schemas/SetDocumentFulltextResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200' patch: @@ -1287,7 +1445,7 @@ schema: $ref: '#/components/schemas/SetDocumentFulltextResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200' /documents/{documentId}/fulltext/tags/{tagKey}: @@ -1304,7 +1462,7 @@ "200": $ref: '#/components/responses/200Cors' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200' /documents/{documentId}/fulltext/tags/{tagKey}/{tagValue}: @@ -1322,7 +1480,7 @@ "200": $ref: '#/components/responses/200Cors' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200' /documents/{documentId}/syncs: @@ -1350,7 +1508,7 @@ schema: $ref: '#/components/schemas/GetDocumentSyncResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' /public/documents: @@ -1444,7 +1602,7 @@ schema: $ref: '#/components/schemas/DocumentId' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' /webhooks: @@ -1473,7 +1631,7 @@ schema: $ref: '#/components/schemas/GetWebhooksResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' post: @@ -1504,7 +1662,7 @@ schema: $ref: '#/components/schemas/AddWebhookResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi201' /webhooks/{webhookId}: @@ -1534,7 +1692,7 @@ schema: $ref: '#/components/schemas/GetWebhookResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' delete: @@ -1549,7 +1707,7 @@ "200": $ref: '#/components/responses/200Cors' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' patch: @@ -1570,7 +1728,7 @@ "200": $ref: '#/components/responses/200Cors' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' /webhooks/{webhookId}/tags: @@ -1597,7 +1755,7 @@ schema: $ref: '#/components/schemas/GetWebhookTagsResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' post: @@ -1626,7 +1784,7 @@ "201": $ref: '#/components/responses/200Cors' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi201' /onlyoffice/{documentId}/edit: @@ -1659,7 +1817,7 @@ schema: $ref: '#/components/schemas/OnlyOfficeDocumentResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/onlyOfficeLambdaApi200' /onlyoffice/new: @@ -1691,7 +1849,7 @@ schema: $ref: '#/components/schemas/OnlyOfficeDocumentResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/onlyOfficeLambdaApi200' /onlyoffice/{documentId}/save: @@ -1755,7 +1913,7 @@ schema: $ref: '#/components/schemas/ValidationErrorsResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' /indices/{indexType}/{indexKey}: @@ -1785,7 +1943,7 @@ schema: $ref: '#/components/schemas/ValidationErrorsResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' /indices/search: @@ -1835,7 +1993,7 @@ schema: $ref: '#/components/schemas/ValidationErrorsResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' /esignature/docusign/{documentId}: @@ -1874,7 +2032,7 @@ schema: $ref: '#/components/schemas/ValidationErrorsResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/esignatureLambdaApi200' /esignature/docusign/config: @@ -1900,7 +2058,7 @@ schema: $ref: '#/components/schemas/EsignatureDocusignConfigResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/esignatureLambdaApi200' put: @@ -1931,7 +2089,7 @@ schema: $ref: '#/components/schemas/EsignatureSetDocusignConfigResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/esignatureLambdaApi200' /esignature/docusign/events: @@ -2035,6 +2193,13 @@ schema: type: string default: "10" + apiKeyParam: + name: apiKey + in: path + description: API Key + required: true + schema: + type: string documentIdParam: name: documentId in: path @@ -2739,6 +2904,9 @@ type: string format: uuid description: Document Identifier + contentLength: + type: integer + description: Document size createdBy: type: string description: User who added document @@ -2767,6 +2935,9 @@ createdBy: type: string description: User who added document + contentLength: + type: integer + description: Document size tags: type: object metadata: @@ -3154,6 +3325,7 @@ - FULLTEXT - ANTIVIRUS - WEBHOOK + - DOCUMENTTAGGING parameters: $ref: '#/components/schemas/AddActionParameters' AddActionParameters: @@ -3171,6 +3343,15 @@ characterMax: type: string description: 'Fulltext: Maximum number of characters (-1 unlimited, Typesense defaults to 2048 characters)' + engine: + type: string + description: 'DocumentTagging: Engine to use for document tagging generation' + enum: + - chatgpt + tags: + description: 'DocumentTagging: List of Tags to generate tags for' + items: + type: string AddDocumentOcrRequest: type: object example: @@ -3215,6 +3396,89 @@ description: List of installed modules items: type: string + SetConfigRequest: + type: object + properties: + chatGptApiKey: + type: string + description: ChatGPT Api Key + maxContentLengthBytes: + type: string + description: Set Maximum Document Content Length in Bytes + maxDocuments: + type: string + description: Set Maximum number of Documents allowed + maxWebhooks: + type: string + description: Set Maximum number of Webhooks allowed + SetConfigResponse: + type: object + properties: + message: + type: string + description: Result message + AddApiKeyRequest: + type: object + properties: + name: + type: string + description: Name of API Key + AddApiKeyResponse: + type: object + properties: + message: + type: string + description: Result message + DeleteApiKeyResponse: + type: object + properties: + message: + type: string + description: Result message + GetApiKeysResponse: + type: object + properties: + apiKeys: + $ref: '#/components/schemas/ApiKeys' + ApiKeys: + type: array + description: List of ApiKeys + items: + $ref: '#/components/schemas/ApiKey' + ApiKey: + type: object + properties: + name: + type: string + description: Name of API Key + apiKey: + type: string + description: API Key value + userId: + type: string + siteIds: + type: array + items: + type: string + insertedDate: + type: string + format: date-time + description: Inserted Timestamp + GetConfigurationResponse: + type: object + properties: + chatGptApiKey: + type: string + description: ChatGPT API Key + maxContentLengthBytes: + type: string + description: Set Maximum Document Content Length in Bytes + maxDocuments: + type: string + description: Set Maximum number of Documents allowed + maxWebhooks: + type: string + description: Set Maximum number of Webhooks allowed GetSitesRequest: type: object properties: @@ -3558,18 +3822,7 @@ type: string description: Message response securitySchemes: - AuthorizationCognito: - type: oauth2 - flows: {} - x-amazon-apigateway-authorizer: - type: jwt - jwtConfiguration: - issuer: - Fn::Sub: https://cognito-idp.${AWS::Region}.amazonaws.com/${CognitoUserPool} - audience: - - Fn::Sub: ${CognitoUserPoolClient} - identitySource: $request.header.Authorization - sigv4: + ApiAuthorization: type: apiKey name: Authorization in: header diff --git a/docs/openapi/openapi-jwt.yaml b/docs/openapi/openapi-jwt.yaml index 403941941..609569355 100644 --- a/docs/openapi/openapi-jwt.yaml +++ b/docs/openapi/openapi-jwt.yaml @@ -6,21 +6,21 @@ url: https://formkiq.com email: support@formkiq.com x-logo: - url: https://docs.formkiq.com/docs/1.9.0/_images/formkiq-logo.png + url: https://docs.formkiq.com/docs/latest/_images/formkiq-logo.png backgroundColor: '#FFFFFF' altText: FormKiQ Logo license: name: Apache 2.0 url: https://www.apache.org/licenses/LICENSE-2.0.html description: FormKiQ HTTP API - version: 1.10.0 + version: 1.11.0 paths: /version: get: operationId: GetVersion description: Return the version of FormKiQ tags: - - Miscellaneous + - System Management responses: "200": description: 200 OK @@ -39,7 +39,7 @@ schema: $ref: '#/components/schemas/GetVersionRequest' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' /sites: @@ -47,7 +47,7 @@ operationId: GetSites description: Returns the list of sites that the user has access to tags: - - Miscellaneous + - System Management responses: "200": description: 200 OK @@ -66,7 +66,165 @@ schema: $ref: '#/components/schemas/GetSitesRequest' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + /configuration: + get: + operationId: GetConfigs + description: Returns the list of sites that the user has access to + tags: + - System Management + parameters: + - $ref: '#/components/parameters/siteIdParam' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + schema: + type: string + Access-Control-Allow-Methods: + schema: + type: string + Access-Control-Allow-Headers: + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/GetConfigurationResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + patch: + operationId: UpdateConfig + description: Update the System Management configuration + tags: + - System Management + parameters: + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SetConfigRequest' + examples: + Config: + value: + chatGptApiKey: ABC + maxContentLengthBytes: "1000000" + maxDocuments: "1000" + maxWebhooks: "10" + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/SetConfigResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + /configuration/apiKeys: + get: + operationId: GetApiKeys + description: Returns the list of ApiKeys + tags: + - System Management + parameters: + - $ref: '#/components/parameters/siteIdParam' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + schema: + type: string + Access-Control-Allow-Methods: + schema: + type: string + Access-Control-Allow-Headers: + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/GetApiKeysResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + post: + operationId: Ã…ddApiKey + description: Adds a new API Key + tags: + - System Management + parameters: + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AddApiKeyRequest' + examples: + AddApiKey: + value: + name: My API Key + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/AddApiKeyResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + /configuration/apiKeys/{apiKey}: + delete: + operationId: DeleteApiKey + description: Adds a new API Key + tags: + - System Management + parameters: + - $ref: '#/components/parameters/apiKeyParam' + - $ref: '#/components/parameters/siteIdParam' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/DeleteApiKeyResponse' + security: + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' /tagSchemas: @@ -106,7 +264,7 @@ schema: $ref: '#/components/schemas/GetTagSchemasRequest' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' post: @@ -150,7 +308,7 @@ schema: $ref: '#/components/schemas/ErrorsResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi201' /tagSchemas/{tagSchemaId}: @@ -177,7 +335,7 @@ schema: $ref: '#/components/schemas/GetTagSchemaRequest' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' delete: @@ -192,7 +350,7 @@ "200": $ref: '#/components/responses/200Cors' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' /documents: @@ -242,7 +400,7 @@ schema: $ref: '#/components/schemas/GetDocumentsResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' post: @@ -314,7 +472,7 @@ schema: $ref: '#/components/schemas/AddDocumentResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi201' /documents/{documentId}: @@ -341,7 +499,7 @@ schema: $ref: '#/components/schemas/GetDocumentResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' patch: @@ -390,7 +548,7 @@ schema: $ref: '#/components/schemas/AddDocumentResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' delete: @@ -405,7 +563,7 @@ "200": $ref: '#/components/responses/200Cors' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' /documents/{documentId}/versions: @@ -437,7 +595,7 @@ schema: $ref: '#/components/schemas/GetDocumentVersionsResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' put: @@ -475,7 +633,7 @@ schema: $ref: '#/components/schemas/ValidationErrorsResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' /documents/{documentId}/versions/{versionKey}: @@ -491,7 +649,7 @@ "200": $ref: '#/components/responses/200Cors' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' /documents/{documentId}/content: @@ -521,7 +679,7 @@ schema: $ref: '#/components/schemas/GetDocumentContentResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' /documents/{documentId}/tags: @@ -559,7 +717,7 @@ schema: $ref: '#/components/schemas/GetDocumentTagsResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' post: @@ -594,7 +752,7 @@ "201": $ref: '#/components/responses/200Cors' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi201' /documents/{documentId}/tags#: @@ -616,7 +774,7 @@ "201": $ref: '#/components/responses/200Cors' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi201' /documents/{documentId}/tags/{tagKey}: @@ -644,7 +802,7 @@ schema: $ref: '#/components/schemas/GetDocumentTagResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' put: @@ -666,7 +824,7 @@ "200": $ref: '#/components/responses/200Cors' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' delete: @@ -682,7 +840,7 @@ "200": $ref: '#/components/responses/200Cors' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' /documents/{documentId}/tags/{tagKey}/{tagValue}: @@ -700,7 +858,7 @@ "200": $ref: '#/components/responses/200Cors' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' /documents/{documentId}/url: @@ -735,7 +893,7 @@ schema: $ref: '#/components/schemas/GetDocumentUrlRequest' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' /documents/upload: @@ -768,7 +926,7 @@ schema: $ref: '#/components/schemas/GetDocumentUrlRequest' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' post: @@ -817,7 +975,7 @@ schema: $ref: '#/components/schemas/AddDocumentResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi201' /documents/{documentId}/upload: @@ -846,7 +1004,7 @@ schema: $ref: '#/components/schemas/GetDocumentUrlRequest' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' /search: @@ -916,7 +1074,7 @@ schema: $ref: '#/components/schemas/DocumentSearchResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/searchLambdaApi200' /searchFulltext: @@ -949,7 +1107,7 @@ schema: $ref: '#/components/schemas/DocumentFulltextResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200' /queryFulltext: @@ -981,7 +1139,7 @@ schema: $ref: '#/components/schemas/QueryFulltextResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200' /documents/{documentId}/actions: @@ -1008,7 +1166,7 @@ schema: $ref: '#/components/schemas/GetDocumentActionsResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' post: @@ -1040,7 +1198,7 @@ schema: $ref: '#/components/schemas/AddDocumentActionsResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' /documents/{documentId}/ocr: @@ -1069,7 +1227,7 @@ schema: $ref: '#/components/schemas/GetDocumentOcrResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/ocrLambdaApi200' post: @@ -1101,7 +1259,7 @@ schema: $ref: '#/components/schemas/AddDocumentOcrResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/ocrLambdaApi200' put: @@ -1133,7 +1291,7 @@ schema: $ref: '#/components/schemas/AddDocumentOcrResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/ocrLambdaApi200' delete: @@ -1148,7 +1306,7 @@ "200": $ref: '#/components/responses/200Cors' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/ocrLambdaApi200' /documents/{documentId}/antivirus: @@ -1181,7 +1339,7 @@ schema: $ref: '#/components/schemas/SetAntivirusResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/antivirusLambdaApi200' /documents/{documentId}/fulltext: @@ -1208,7 +1366,7 @@ schema: $ref: '#/components/schemas/GetDocumentFulltextResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200' delete: @@ -1223,7 +1381,7 @@ "200": $ref: '#/components/responses/200Cors' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200' put: @@ -1255,7 +1413,7 @@ schema: $ref: '#/components/schemas/SetDocumentFulltextResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200' patch: @@ -1287,7 +1445,7 @@ schema: $ref: '#/components/schemas/SetDocumentFulltextResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200' /documents/{documentId}/fulltext/tags/{tagKey}: @@ -1304,7 +1462,7 @@ "200": $ref: '#/components/responses/200Cors' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200' /documents/{documentId}/fulltext/tags/{tagKey}/{tagValue}: @@ -1322,7 +1480,7 @@ "200": $ref: '#/components/responses/200Cors' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200' /documents/{documentId}/syncs: @@ -1350,7 +1508,7 @@ schema: $ref: '#/components/schemas/GetDocumentSyncResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' /public/documents: @@ -1444,7 +1602,7 @@ schema: $ref: '#/components/schemas/DocumentId' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' /webhooks: @@ -1473,7 +1631,7 @@ schema: $ref: '#/components/schemas/GetWebhooksResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' post: @@ -1504,7 +1662,7 @@ schema: $ref: '#/components/schemas/AddWebhookResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi201' /webhooks/{webhookId}: @@ -1534,7 +1692,7 @@ schema: $ref: '#/components/schemas/GetWebhookResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' delete: @@ -1549,7 +1707,7 @@ "200": $ref: '#/components/responses/200Cors' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' patch: @@ -1570,7 +1728,7 @@ "200": $ref: '#/components/responses/200Cors' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' /webhooks/{webhookId}/tags: @@ -1597,7 +1755,7 @@ schema: $ref: '#/components/schemas/GetWebhookTagsResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' post: @@ -1626,7 +1784,7 @@ "201": $ref: '#/components/responses/200Cors' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi201' /onlyoffice/{documentId}/edit: @@ -1659,7 +1817,7 @@ schema: $ref: '#/components/schemas/OnlyOfficeDocumentResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/onlyOfficeLambdaApi200' /onlyoffice/new: @@ -1691,7 +1849,7 @@ schema: $ref: '#/components/schemas/OnlyOfficeDocumentResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/onlyOfficeLambdaApi200' /onlyoffice/{documentId}/save: @@ -1755,7 +1913,7 @@ schema: $ref: '#/components/schemas/ValidationErrorsResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' /indices/{indexType}/{indexKey}: @@ -1785,7 +1943,7 @@ schema: $ref: '#/components/schemas/ValidationErrorsResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' /indices/search: @@ -1835,7 +1993,7 @@ schema: $ref: '#/components/schemas/ValidationErrorsResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' /esignature/docusign/{documentId}: @@ -1874,7 +2032,7 @@ schema: $ref: '#/components/schemas/ValidationErrorsResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/esignatureLambdaApi200' /esignature/docusign/config: @@ -1900,7 +2058,7 @@ schema: $ref: '#/components/schemas/EsignatureDocusignConfigResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/esignatureLambdaApi200' put: @@ -1931,7 +2089,7 @@ schema: $ref: '#/components/schemas/EsignatureSetDocusignConfigResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: '#/components/x-amazon-apigateway-integrations/esignatureLambdaApi200' /esignature/docusign/events: @@ -2035,6 +2193,13 @@ schema: type: string default: "10" + apiKeyParam: + name: apiKey + in: path + description: API Key + required: true + schema: + type: string documentIdParam: name: documentId in: path @@ -2739,6 +2904,9 @@ type: string format: uuid description: Document Identifier + contentLength: + type: integer + description: Document size createdBy: type: string description: User who added document @@ -2767,6 +2935,9 @@ createdBy: type: string description: User who added document + contentLength: + type: integer + description: Document size tags: type: object metadata: @@ -3154,6 +3325,7 @@ - FULLTEXT - ANTIVIRUS - WEBHOOK + - DOCUMENTTAGGING parameters: $ref: '#/components/schemas/AddActionParameters' AddActionParameters: @@ -3171,6 +3343,15 @@ characterMax: type: string description: 'Fulltext: Maximum number of characters (-1 unlimited, Typesense defaults to 2048 characters)' + engine: + type: string + description: 'DocumentTagging: Engine to use for document tagging generation' + enum: + - chatgpt + tags: + description: 'DocumentTagging: List of Tags to generate tags for' + items: + type: string AddDocumentOcrRequest: type: object example: @@ -3215,6 +3396,89 @@ description: List of installed modules items: type: string + SetConfigRequest: + type: object + properties: + chatGptApiKey: + type: string + description: ChatGPT Api Key + maxContentLengthBytes: + type: string + description: Set Maximum Document Content Length in Bytes + maxDocuments: + type: string + description: Set Maximum number of Documents allowed + maxWebhooks: + type: string + description: Set Maximum number of Webhooks allowed + SetConfigResponse: + type: object + properties: + message: + type: string + description: Result message + AddApiKeyRequest: + type: object + properties: + name: + type: string + description: Name of API Key + AddApiKeyResponse: + type: object + properties: + message: + type: string + description: Result message + DeleteApiKeyResponse: + type: object + properties: + message: + type: string + description: Result message + GetApiKeysResponse: + type: object + properties: + apiKeys: + $ref: '#/components/schemas/ApiKeys' + ApiKeys: + type: array + description: List of ApiKeys + items: + $ref: '#/components/schemas/ApiKey' + ApiKey: + type: object + properties: + name: + type: string + description: Name of API Key + apiKey: + type: string + description: API Key value + userId: + type: string + siteIds: + type: array + items: + type: string + insertedDate: + type: string + format: date-time + description: Inserted Timestamp + GetConfigurationResponse: + type: object + properties: + chatGptApiKey: + type: string + description: ChatGPT API Key + maxContentLengthBytes: + type: string + description: Set Maximum Document Content Length in Bytes + maxDocuments: + type: string + description: Set Maximum number of Documents allowed + maxWebhooks: + type: string + description: Set Maximum number of Webhooks allowed GetSitesRequest: type: object properties: @@ -3558,7 +3822,7 @@ type: string description: Message response securitySchemes: - AuthorizationCognito: + ApiAuthorization: type: oauth2 flows: {} x-amazon-apigateway-authorizer: diff --git a/docs/openapi/openapi-key.yaml b/docs/openapi/openapi-key.yaml new file mode 100644 index 000000000..37c0d1469 --- /dev/null +++ b/docs/openapi/openapi-key.yaml @@ -0,0 +1,3965 @@ + openapi: 3.0.1 + info: + title: FormKiQ KEY API + contact: + name: FormKiQ + url: https://formkiq.com + email: support@formkiq.com + x-logo: + url: https://docs.formkiq.com/docs/latest/_images/formkiq-logo.png + backgroundColor: '#FFFFFF' + altText: FormKiQ Logo + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html + description: FormKiQ Api Key + version: 1.11.0 + paths: + /version: + get: + operationId: GetVersion + description: Return the version of FormKiQ + tags: + - System Management + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + schema: + type: string + Access-Control-Allow-Methods: + schema: + type: string + Access-Control-Allow-Headers: + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/GetVersionRequest' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + /sites: + get: + operationId: GetSites + description: Returns the list of sites that the user has access to + tags: + - System Management + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + schema: + type: string + Access-Control-Allow-Methods: + schema: + type: string + Access-Control-Allow-Headers: + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/GetSitesRequest' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + /configuration: + get: + operationId: GetConfigs + description: Returns the list of sites that the user has access to + tags: + - System Management + parameters: + - $ref: '#/components/parameters/siteIdParam' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + schema: + type: string + Access-Control-Allow-Methods: + schema: + type: string + Access-Control-Allow-Headers: + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/GetConfigurationResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + patch: + operationId: UpdateConfig + description: Update the System Management configuration + tags: + - System Management + parameters: + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SetConfigRequest' + examples: + Config: + value: + chatGptApiKey: ABC + maxContentLengthBytes: "1000000" + maxDocuments: "1000" + maxWebhooks: "10" + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/SetConfigResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + /configuration/apiKeys: + get: + operationId: GetApiKeys + description: Returns the list of ApiKeys + tags: + - System Management + parameters: + - $ref: '#/components/parameters/siteIdParam' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + schema: + type: string + Access-Control-Allow-Methods: + schema: + type: string + Access-Control-Allow-Headers: + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/GetApiKeysResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + post: + operationId: Ã…ddApiKey + description: Adds a new API Key + tags: + - System Management + parameters: + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AddApiKeyRequest' + examples: + AddApiKey: + value: + name: My API Key + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/AddApiKeyResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + /configuration/apiKeys/{apiKey}: + delete: + operationId: DeleteApiKey + description: Adds a new API Key + tags: + - System Management + parameters: + - $ref: '#/components/parameters/apiKeyParam' + - $ref: '#/components/parameters/siteIdParam' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/DeleteApiKeyResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + /tagSchemas: + get: + operationId: GetTagSchemas + description: Returns the list of tagSchemas; ONLY available with FormKiQ Pro and Enterprise + tags: + - Tag Schema + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/limitParam' + - name: next + in: query + description: Next page of results token + schema: + type: string + - name: previous + in: query + description: Previous page of results token + schema: + type: string + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + schema: + type: string + Access-Control-Allow-Methods: + schema: + type: string + Access-Control-Allow-Headers: + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/GetTagSchemasRequest' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + post: + operationId: AddTagSchema + description: Creates a new TagSchema; ONLY available with FormKiQ Pro and Enterprise + tags: + - Tag Schema + parameters: + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AddTagSchemaRequest' + responses: + "201": + description: 201 CREATED + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/TagSchemaPostResponse' + "400": + description: 400 BAD REQUEST + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorsResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi201' + /tagSchemas/{tagSchemaId}: + get: + operationId: GetTagSchema + description: Retrieves a TagSchema's details, i.e., metadata; ONLY available with FormKiQ Pro and Enterprise + tags: + - Tag Schema + parameters: + - $ref: '#/components/parameters/tagSchemaIdParam' + - $ref: '#/components/parameters/siteIdParam' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/GetTagSchemaRequest' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + delete: + operationId: DeleteTagSchema + description: Delete a TagSchema; ONLY available with FormKiQ Pro and Enterprise + tags: + - Tag Schema + parameters: + - $ref: '#/components/parameters/tagSchemaIdParam' + - $ref: '#/components/parameters/siteIdParam' + responses: + "200": + $ref: '#/components/responses/200Cors' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + /documents: + get: + operationId: GetDocuments + tags: + - Documents + description: Returns a list of the most recent documents added, ordered by inserted, descending + parameters: + - name: date + in: query + description: Fetch documents inserted on a certain date (yyyy-MM-dd) + schema: + type: string + - name: tz + in: query + description: 'UTC offset to apply to date parameter (IE: -0600)' + schema: + type: string + - name: next + in: query + description: Next page of results token + schema: + type: string + - name: previous + in: query + description: Previous page of results token + schema: + type: string + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/limitParam' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + schema: + type: string + Access-Control-Allow-Methods: + schema: + type: string + Access-Control-Allow-Headers: + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/GetDocumentsResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + post: + operationId: AddDocument + description: Creates a new document; body may include document content if less than 5 MB + tags: + - Documents + parameters: + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AddDocumentRequest' + examples: + Simple_File: + value: + path: test.txt + contentType: text/plain + isBase64: false + content: This is sample data file + tags: + - key: category + value: sample + - key: players + values: + - "111" + - "222" + metadata: + - key: info + value: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Nested_Document: + value: + path: doc1.txt + contentType: text/plain + content: This is document1 content + tags: + - key: type + value: document1 + documents: + contentType: text/plain, + content: This is sub document1 content, + tags: + - key: type + value: subdocument1 + Document_with_Webhook: + value: + path: test.txt + contentType: text/plain + isBase64: false + content: This is sample data file + actions: + - type: webhook + parameters: + url: + responses: + "201": + description: 201 CREATED + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/AddDocumentResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi201' + /documents/{documentId}: + get: + operationId: GetDocument + description: Retrieves a document's details, i.e., metadata + tags: + - Documents + parameters: + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/siteIdParam' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/GetDocumentResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + patch: + operationId: UpdateDocument + description: Update a document's details, i.e., metadata + tags: + - Documents + parameters: + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AddDocumentRequest' + examples: + Simple_File: + value: + path: test.txt + contentType: text/plain + isBase64: false + content: This is sample data file + tags: + - key: category + value: sample + - key: players + values: + - "111" + - "222" + metadata: + - key: info + value: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/AddDocumentResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + delete: + operationId: DeleteDocument + description: Delete a document + tags: + - Documents + parameters: + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/siteIdParam' + responses: + "200": + $ref: '#/components/responses/200Cors' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + /documents/{documentId}/versions: + get: + operationId: GetDocumentVersions + description: Get a listing of document content and metadata versions; ONLY available with FormKiQ Pro and Enterprise + tags: + - Document Versions + parameters: + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/siteIdParam' + - name: next + in: query + description: Next page of results token + schema: + type: string + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/GetDocumentVersionsResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + put: + operationId: SetDocumentVersion + description: Set document to a previous document version; ONLY available with FormKiQ Pro and Enterprise + tags: + - Document Versions + parameters: + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SetDocumentVersionRequest' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/SetDocumentVersionResponse' + "400": + description: 400 OK + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationErrorsResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + /documents/{documentId}/versions/{versionKey}: + delete: + operationId: DeleteDocumentVersion + description: Delete a specific previous document version; ONLY available with FormKiQ Pro and Enterprise + tags: + - Document Versions + parameters: + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/versionKeyPath' + responses: + "200": + $ref: '#/components/responses/200Cors' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + /documents/{documentId}/content: + get: + operationId: GetDocumentContent + description: Get a document's contents. Certain content types, text/*, application/json, and application/x-www-form-urlencoded. return the "content" field, while all other content types return a 'contentUrl' for retrieving the content. + tags: + - Documents + parameters: + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/versionKeyParam' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/GetDocumentContentResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + /documents/{documentId}/tags: + get: + operationId: GetDocumentTags + description: Get a listing of a document's tags + tags: + - Document Tags + parameters: + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/limitParam' + - name: next + in: query + description: Next page of results token + schema: + type: string + - name: previous + in: query + description: Previous page of results token + schema: + type: string + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/GetDocumentTagsResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + post: + operationId: AddDocumentTag + description: Add a single tag to a document; this endpoint also accepts a different body parameter for adding multiple tags in a single request + tags: + - Document Tags + parameters: + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AddDocumentTagRequest' + examples: + Add_Key_Only_Tag: + value: + key: category + Add_Key_and_Value_Tag: + value: + key: category + value: person + Add_Key_and_Values_Tag: + value: + key: category + values: + - person + - canadian + responses: + "201": + $ref: '#/components/responses/200Cors' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi201' + /documents/{documentId}/tags#: + post: + operationId: AddDocumentTags + description: Add multiple tags to a document; this endpoint also accepts a different body parameter for adding a single tag + tags: + - Document Tags + parameters: + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AddDocumentTagsRequest' + responses: + "201": + $ref: '#/components/responses/200Cors' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi201' + /documents/{documentId}/tags/{tagKey}: + get: + operationId: GetDocumentTag + description: Get a document tag by using its key + tags: + - Document Tags + parameters: + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/tagkeyParam' + - $ref: '#/components/parameters/siteIdParam' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/GetDocumentTagResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + put: + operationId: SetDocumentTag + description: Update any and all values of a document tag, by using its key; you can supply one tag value or a list of tag values in the request body + tags: + - Document Tags + parameters: + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/tagkeyParam' + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SetDocumentTagKeyRequest' + responses: + "200": + $ref: '#/components/responses/200Cors' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + delete: + operationId: DeleteDocumentTag + description: Delete a document tag by using its key + tags: + - Document Tags + parameters: + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/tagkeyParam' + - $ref: '#/components/parameters/siteIdParam' + responses: + "200": + $ref: '#/components/responses/200Cors' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + /documents/{documentId}/tags/{tagKey}/{tagValue}: + delete: + operationId: DeleteDocumentTagAndValue + description: Delete a specific document tag's key/value combination; the request will be ignored if there is no valid key/value combination found + tags: + - Document Tags + parameters: + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/tagkeyParam' + - $ref: '#/components/parameters/tagvalueParam' + - $ref: '#/components/parameters/siteIdParam' + responses: + "200": + $ref: '#/components/responses/200Cors' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + /documents/{documentId}/url: + get: + operationId: GetDocumentUrl + description: Returns a URL for the document's contents; this URL will expire (the default is 48 hours) + tags: + - Documents + parameters: + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/versionKeyParam' + - $ref: '#/components/parameters/durationParam' + - name: inline + in: query + description: Set the Content-Disposition to inline + schema: + type: boolean + default: false + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/GetDocumentUrlRequest' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + /documents/upload: + get: + operationId: GetDocumentUpload + description: Returns a URL that can be used to upload document content and create a new document; this endpoint (whether GET or POST) is required in order to add content that is larger than 5 MB + tags: + - Documents + parameters: + - name: path + in: query + description: The upload file's path + schema: + type: string + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/contentLengthParam' + - $ref: '#/components/parameters/durationParam' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/GetDocumentUrlRequest' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + post: + operationId: AddDocumentUpload + description: Returns a URL that can be used to upload document content and create a new document, while allowing metadata to also be sent; this endpoint (whether GET or POST) is required in order to add content that is larger than 5 MB + tags: + - Documents + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/durationParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AddDocumentUploadRequest' + examples: + Simple_File: + value: + path: test.txt + contentType: text/plain + isBase64: false + content: This is sample data file + tags: + - key: category + value: sample + - key: players + values: + - "111" + - "222" + metadata: + - key: info + value: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + responses: + "201": + description: 201 CREATED + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/AddDocumentResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi201' + /documents/{documentId}/upload: + get: + operationId: GetDocumentIdUpload + parameters: + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/contentLengthParam' + - $ref: '#/components/parameters/durationParam' + description: Returns a URL that can be used to upload documents for a specific documentId; this endpoint is required in order to add content that is larger than 5 MB. If versions are enabled, this will create a new version of the document. + tags: + - Documents + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/GetDocumentUrlRequest' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + /search: + post: + operationId: DocumentSearch + description: Document search query request; documents are searched primarily using a document tag key and optional tag value, or if Typesense is enabled, searches on the document path and versioned metadata is also available. An optional documentIds parameter is also available in the DocumentSearchBody to filter, with up to 100 documentIds accepted. + tags: + - Document Search + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/limitParam' + - name: next + in: query + description: Next page of results token + schema: + type: string + - name: previous + in: query + description: Previous page of results token + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentSearchRequest' + examples: + Search_By_Key: + value: + query: + tag: + key: category + Search_By_Value: + value: + query: + tag: + key: category + eq: person + Tag_Value_Begins_With: + value: + query: + tag: + key: firstname + beginsWith: jo + Search_By_Value_for_Specific_Documents: + value: + query: + tag: + key: category + eq: person + documentIds: + - "1" + - "2" + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentSearchResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/searchLambdaApi200' + /searchFulltext: + post: + operationId: DocumentFulltext + description: Document full-text search (and more robust multi-tag search queries, powered by OpenSearch); ONLY available with FormKiQ Enterprise + tags: + - Advanced Document Search + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/limitParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentFulltextRequest' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentFulltextResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200' + /queryFulltext: + post: + operationId: QueryFulltext + description: Endpoint for allowing custom, complex queries using the OpenSearch search API (https://opensearch.org/docs/latest/opensearch/rest-api/search/); ONLY available with FormKiQ Enterprise + tags: + - Advanced Document Search + parameters: + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/QueryFulltextRequest' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/QueryFulltextResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200' + /documents/{documentId}/actions: + get: + operationId: GetDocumentActions + description: Get document actions and their status + tags: + - Document Actions + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/documentIdParam' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/GetDocumentActionsResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + post: + operationId: AddDocumentActions + description: Add one or more actions to a document; this appends actions and does not replace previous actions + tags: + - Document Actions + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/documentIdParam' + requestBody: + required: false + content: + application/json: + schema: + $ref: '#/components/schemas/AddDocumentActionsRequest' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/AddDocumentActionsResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + /documents/{documentId}/ocr: + get: + operationId: GetDocumentOcr + description: Get a document's optical character recognition (OCR) result, if exists; ONLY available with FormKiQ Pro and Enterprise + tags: + - OCR + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/contentUrlParam' + - $ref: '#/components/parameters/textParam' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/GetDocumentOcrResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/ocrLambdaApi200' + post: + operationId: AddDocumentOcr + description: Document optical character recognition (OCR) request; extract text and data from a document; ONLY available with FormKiQ Pro and Enterprise + tags: + - OCR + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/documentIdParam' + requestBody: + required: false + content: + application/json: + schema: + $ref: '#/components/schemas/AddDocumentOcrRequest' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/AddDocumentOcrResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/ocrLambdaApi200' + put: + operationId: SetDocumentOcr + description: Set a document's optical character recognition (OCR) result for a document; ONLY available with FormKiQ Pro and Enterprise + tags: + - OCR + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/documentIdParam' + requestBody: + required: false + content: + application/json: + schema: + $ref: '#/components/schemas/SetDocumentOcrRequest' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/AddDocumentOcrResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/ocrLambdaApi200' + delete: + operationId: DeleteDocumentOcr + description: Delete a document's optical character recognition (OCR) result, if exists; ONLY available with FormKiQ Pro and Enterprise + tags: + - OCR + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/documentIdParam' + responses: + "200": + $ref: '#/components/responses/200Cors' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/ocrLambdaApi200' + /documents/{documentId}/antivirus: + put: + operationId: SetAntivirus + description: Perform an Anti-Malware / Antivirus scan on a document; ONLY available with FormKiQ Pro and Enterprise + tags: + - Antivirus + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/documentIdParam' + requestBody: + required: false + content: + application/json: + schema: + $ref: '#/components/schemas/SetAntivirusRequest' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/SetAntivirusResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/antivirusLambdaApi200' + /documents/{documentId}/fulltext: + get: + operationId: GetDocumentFulltext + description: Retrieve an OpenSearch document's details, i.e., metadata + tags: + - Advanced Document Search + parameters: + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/siteIdParam' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/GetDocumentFulltextResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200' + delete: + operationId: DeleteDocumentFulltext + description: Remove full text search for a document from OpenSearch; ONLY available with FormKiQ Enterprise + tags: + - Advanced Document Search + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/documentIdParam' + responses: + "200": + $ref: '#/components/responses/200Cors' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200' + put: + operationId: SetDocumentFulltext + description: Set all text/tags found within a document to OpenSearch; ONLY available with FormKiQ Enterprise + tags: + - Advanced Document Search + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/documentIdParam' + requestBody: + required: false + content: + application/json: + schema: + $ref: '#/components/schemas/SetDocumentFulltextRequest' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/SetDocumentFulltextResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200' + patch: + operationId: UpdateDocumentFulltext + description: Update a document in OpenSearch; ONLY available with FormKiQ Enterprise + tags: + - Advanced Document Search + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/documentIdParam' + requestBody: + required: false + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateDocumentFulltextRequest' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/SetDocumentFulltextResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200' + /documents/{documentId}/fulltext/tags/{tagKey}: + delete: + operationId: DeleteDocumentFulltextTag + description: Remove document tags from OpenSearch; ONLY available with FormKiQ Enterprise + tags: + - Advanced Document Search + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/tagkeyParam' + responses: + "200": + $ref: '#/components/responses/200Cors' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200' + /documents/{documentId}/fulltext/tags/{tagKey}/{tagValue}: + delete: + operationId: DeleteDocumentFulltextTagAndValue + description: Remove document tag/value from OpenSearch; ONLY available with FormKiQ Enterprise + tags: + - Advanced Document Search + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/tagkeyParam' + - $ref: '#/components/parameters/tagvalueParam' + responses: + "200": + $ref: '#/components/responses/200Cors' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200' + /documents/{documentId}/syncs: + get: + operationId: GetDocumentSyncs + description: Retrieve the document syncs with external services status + tags: + - Document Synchronization + parameters: + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/limitParam' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/GetDocumentSyncResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + /public/documents: + post: + operationId: PublicAddDocument + description: Allow unauthenticated creation of new documents; must be enabled during installation (disabled by default) + tags: + - Public + parameters: + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AddDocumentRequest' + responses: + "201": + description: 201 CREATED + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentId' + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi201' + /public/webhooks/{webhooks+}: + post: + operationId: PublicAddWebhook + description: Receive an incoming public post to a specified webhook and creates a document based on the data sent; must be enabled during installation (disabled by default) + tags: + - Public + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/webhooksParam' + requestBody: + required: true + content: + application/json: + schema: + type: object + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentId' + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + /private/webhooks/{webhooks+}: + post: + operationId: AddWebhookDocument + description: Receive an incoming private webhook and creates a document based on the webhook's body; requires authentication + tags: + - Webhooks + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/webhooksParam' + requestBody: + required: true + content: + application/json: + schema: + type: object + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentId' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + /webhooks: + get: + operationId: GetWebhooks + tags: + - Webhooks + description: Return a list of webhooks; each webhook's id can be provided to an external service, allowing data to be sent, received, and processed via that webhook + parameters: + - $ref: '#/components/parameters/siteIdParam' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + schema: + type: string + Access-Control-Allow-Methods: + schema: + type: string + Access-Control-Allow-Headers: + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/GetWebhooksResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + post: + operationId: AddWebhook + description: Create a new webhook; once created, a webhook's id can be provided to an external service, allowing data to be sent, received, and processed via that webhook + tags: + - Webhooks + parameters: + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AddWebhookRequest' + responses: + "201": + description: 201 CREATED + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/AddWebhookResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi201' + /webhooks/{webhookId}: + get: + operationId: GetWebhook + tags: + - Webhooks + description: Return a webhook's details, i.e., its metadata + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/webhookIdParam' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + schema: + type: string + Access-Control-Allow-Methods: + schema: + type: string + Access-Control-Allow-Headers: + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/GetWebhookResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + delete: + operationId: DeleteWebhook + description: Delete a webhook; this will disable sending, receiving, or processing of data from external services to this webhook + tags: + - Webhooks + parameters: + - $ref: '#/components/parameters/webhookIdParam' + - $ref: '#/components/parameters/siteIdParam' + responses: + "200": + $ref: '#/components/responses/200Cors' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + patch: + operationId: UpdateWebhook + description: Updates a webhook's details, i.e., its metadata + tags: + - Webhooks + parameters: + - $ref: '#/components/parameters/webhookIdParam' + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AddWebhookRequest' + responses: + "200": + $ref: '#/components/responses/200Cors' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + /webhooks/{webhookId}/tags: + get: + operationId: GetWebhookTags + description: Get a webhook's tags + tags: + - Webhooks + parameters: + - $ref: '#/components/parameters/webhookIdParam' + - $ref: '#/components/parameters/siteIdParam' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/GetWebhookTagsResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + post: + operationId: AddWebhookTag + description: Add a tag to a webhook + tags: + - Webhooks + parameters: + - $ref: '#/components/parameters/webhookIdParam' + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/GetDocumentTagResponse' + examples: + Add_Key_Only_Tag: + value: + key: category + Add_Key_and_Value_Tag: + value: + key: category + value: person + responses: + "201": + $ref: '#/components/responses/200Cors' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi201' + /onlyoffice/{documentId}/edit: + post: + operationId: OnlyOfficeDocumentEdit + description: Provide ONLYOFFICE integration for editing documents; ONLY available with FormKiQ Enterprise + tags: + - Onlyoffice + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/documentIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/OnlyOfficeDocumentEditRequest' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/OnlyOfficeDocumentResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/onlyOfficeLambdaApi200' + /onlyoffice/new: + post: + operationId: OnlyOfficeDocumentNew + description: Provide ONLYOFFICE integration for the creation of new documents; ONLY available with FormKiQ Enterprise + tags: + - Onlyoffice + parameters: + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/OnlyOfficeDocumentNewRequest' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/OnlyOfficeDocumentResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/onlyOfficeLambdaApi200' + /onlyoffice/{documentId}/save: + post: + operationId: OnlyOfficeDocumentSave + description: Save an updated document for ONLYOFFICE integration. ONLY available with FormKiQ Enterprise + tags: + - Onlyoffice + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/documentIdParam' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/OnlyOfficeDocumentSaveResponse' + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/onlyOfficeLambdaApi200' + /indices/{indexType}/move: + post: + operationId: IndexFolderMove + description: Perform an Folder Index Move + tags: + - Folder Index + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/indexTypeParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/IndexFolderMoveRequest' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/IndexFolderMoveResponse' + "400": + description: 400 OK + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationErrorsResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + /indices/{indexType}/{indexKey}: + delete: + operationId: DeleteIndex + description: Perform a delete on the Folder Index + tags: + - Folder Index + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/indexKeyParam' + - $ref: '#/components/parameters/indexTypeParam' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + "400": + description: 400 OK + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationErrorsResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + /indices/search: + post: + operationId: IndexSearch + description: Perform a search on a index; this is currently available for both folder and tag indices + tags: + - Folder Index + - Tag Index + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/limitParam' + - name: next + in: query + description: Next page of results token + schema: + type: string + - name: previous + in: query + description: Previous page of results token + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/IndexSearchRequest' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/IndexSearchResponse' + "400": + description: 400 OK + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationErrorsResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/lambdaApi200' + /esignature/docusign/{documentId}: + post: + operationId: EsignatureDocusign + description: Create a DocuSign E-Signature request; ONLY available with FormKiQ Enterprise + tags: + - E-Signature + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/documentIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/EsignatureDocusignRequest' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/EsignatureDocusignResponse' + "400": + description: 400 OK + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationErrorsResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/esignatureLambdaApi200' + /esignature/docusign/config: + get: + operationId: EsignatureDocusignConfig + description: Get DocuSign configuration info; ONLY available with FormKiQ Enterprise + tags: + - E-Signature + parameters: + - $ref: '#/components/parameters/siteIdParam' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/EsignatureDocusignConfigResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/esignatureLambdaApi200' + put: + operationId: EsignatureSetDocusignConfig + description: Set DocuSign configuration, required for integration; ONLY available with FormKiQ Enterprise + tags: + - E-Signature + parameters: + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/EsignatureSetDocusignConfigRequest' + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/EsignatureSetDocusignConfigResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/esignatureLambdaApi200' + /esignature/docusign/events: + post: + operationId: AddEsignatureDocusignEvents + description: DocuSign callback URL handler; ONLY available with FormKiQ Enterprise + tags: + - E-Signature + responses: + "200": + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/EsignatureDocusignResponse' + x-amazon-apigateway-integration: + $ref: '#/components/x-amazon-apigateway-integrations/esignatureLambdaApi200' + components: + headers: + AccessControlAllowOrigin: + schema: + type: string + AccessControlAllowMethods: + schema: + type: string + AccessControlAllowHeaders: + schema: + type: string + Location: + schema: + type: string + responses: + 200Cors: + description: 200 OK + headers: + Access-Control-Allow-Origin: + schema: + type: string + Access-Control-Allow-Methods: + schema: + type: string + Access-Control-Allow-Headers: + schema: + type: string + content: {} + parameters: + versionKeyPath: + name: versionKey + in: path + description: Version Key + required: true + schema: + type: string + versionKeyParam: + name: versionKey + in: query + description: Version Key + required: false + schema: + type: string + webhooksParam: + name: webhooks+ + in: path + required: true + description: Web Hook Param + schema: + type: string + webhookIdParam: + name: webhookId + in: path + required: true + description: Web Hook Param + schema: + type: string + siteIdParam: + name: siteId + in: query + description: Site Identifier + required: false + schema: + type: string + providerParam: + name: provider + in: query + description: Provider Identifier + required: true + schema: + type: string + limitParam: + name: limit + in: query + description: Limit Results + required: false + schema: + type: string + default: "10" + apiKeyParam: + name: apiKey + in: path + description: API Key + required: true + schema: + type: string + documentIdParam: + name: documentId + in: path + description: Document Identifier + required: true + schema: + type: string + tagSchemaIdParam: + name: tagSchemaId + in: path + description: Tag Schema Identifier + required: true + schema: + type: string + tagkeyParam: + name: tagKey + in: path + description: Tag Key + required: true + schema: + type: string + tagvalueParam: + name: tagValue + in: path + description: Tag Key Value + required: true + schema: + type: string + contentLengthParam: + name: contentLength + in: query + description: Indicates the size of the entity-body + required: false + schema: + type: integer + durationParam: + name: duration + in: query + description: Indicates the number of hours request is valid for + schema: + type: integer + contentUrlParam: + name: contentUrl + in: query + description: Whether to return a "contentUrl", set value to 'true' + required: false + schema: + type: string + textParam: + name: text + in: query + description: Returns raw 'text' of OCR content. e.g. AWS Textract returns JSON, setting parameter to 'true' converts JSON to Text + required: false + schema: + type: string + indexKeyParam: + name: indexKey + in: path + description: Index Key Identifier + required: true + schema: + type: string + indexTypeParam: + name: indexType + in: path + description: Index Type + required: true + schema: + type: string + schemas: + ValidationErrorsResponse: + type: object + properties: + errors: + $ref: '#/components/schemas/ValidationErrors' + ValidationErrors: + type: array + description: List of errors + items: + $ref: '#/components/schemas/ValidationError' + ValidationError: + type: object + properties: + key: + type: string + description: Error Key + error: + type: string + description: Error Message + IndexFolderMoveRequest: + type: object + properties: + source: + type: string + description: Source path + target: + type: string + description: Target path + SetDocumentTagKeyRequest: + type: object + properties: + value: + type: string + description: Tag value + values: + type: array + description: Tag values + items: + type: string + IndexFolderMoveResponse: + type: object + properties: + message: + type: string + description: Folder move message + IndexSearchRequest: + type: object + properties: + indexType: + type: string + description: The name of the index to search + IndexSearchResponse: + type: object + properties: + next: + type: string + description: Next page of results token + previous: + type: string + description: Previous page of results token + values: + $ref: '#/components/schemas/IndexSearchItems' + IndexSearchItems: + type: array + description: List of search results + items: + $ref: '#/components/schemas/IndexSearch' + IndexSearch: + type: object + properties: + value: + type: string + description: value of index + GetDocumentContentResponse: + type: object + properties: + content: + type: string + description: Document content + contentUrl: + type: string + description: URL to retrieve document content + contentType: + type: string + description: Document Content-Type + isBase64: + type: boolean + description: Is the content Base64-encoded? + GetDocumentVersionsResponse: + type: object + properties: + next: + type: string + description: Next page of results token + versions: + $ref: '#/components/schemas/DocumentItemVersions' + GetDocumentTagsResponse: + type: object + properties: + next: + type: string + description: Next page of results token + previous: + type: string + description: Previous page of results token + tags: + $ref: '#/components/schemas/DocumentItemTags' + SetDocumentVersionRequest: + type: object + properties: + versionKey: + type: string + description: VersionKey returned by the GET /documents/{documentId}/versions to revert to + SetDocumentVersionResponse: + type: object + properties: + message: + type: string + description: Response Message + DocumentItemTags: + type: array + description: List of tags + items: + $ref: '#/components/schemas/GetDocumentTagResponse' + DocumentItemVersions: + type: array + description: List of document versions + items: + $ref: '#/components/schemas/DocumentItemVersion' + DocumentSearch: + type: object + description: Document tag search criteria + properties: + text: + type: string + description: Full text search + meta: + $ref: '#/components/schemas/DocumentSearchItemMeta' + tag: + $ref: '#/components/schemas/DocumentSearchItemTag' + documentIds: + type: array + description: List of DocumentIds to filter search results on + items: + type: string + DocumentId: + required: + - documentId + type: object + properties: + documentId: + type: string + format: uuid + description: Document Identifier + siteId: + type: string + description: Site Identifier + AddWebhookResponse: + type: object + properties: + id: + type: string + format: uuid + siteId: + type: string + description: Site Identifier + AddDocumentResponse: + type: object + properties: + documentId: + type: string + format: uuid + description: Document Identifier + siteId: + type: string + description: Site Identifier + uploadUrl: + type: string + description: Url to upload document to + documents: + $ref: '#/components/schemas/AddChildDocumentsResponse' + AddChildDocumentsResponse: + type: array + description: List of child documents + items: + $ref: '#/components/schemas/AddChildDocumentResponse' + AddChildDocumentResponse: + type: object + properties: + documentId: + type: string + description: Document Identifier + uploadUrl: + type: string + description: Url to upload document to + GetDocumentUrlRequest: + type: object + properties: + documentId: + type: string + description: Document Identifier + url: + type: string + description: Document content url + GetDocumentResponse: + required: + - documentId + - path + type: object + properties: + next: + type: string + description: Next page of results token + previous: + type: string + description: Previous page of results token + siteId: + type: string + description: Site Identifier + path: + type: string + description: Path or Name of document + insertedDate: + type: string + format: date-time + description: Inserted Timestamp + lastModifiedDate: + type: string + format: date-time + description: Last Modified Timestamp + checksum: + type: string + description: Document checksum, changes when document file changes + documentId: + type: string + format: uuid + description: Document Identifier + contentType: + type: string + description: Document Content-Type + userId: + type: string + description: User who added document + contentLength: + type: integer + description: Document size + versionId: + type: string + description: Document version + belongsToDocumentId: + type: string + description: Parent Document Identifier + documents: + type: array + description: List of related documents + items: + type: object + properties: + path: + type: string + description: Path or Name of document + insertedDate: + type: string + format: date-time + description: Inserted Timestamp + lastModifiedDate: + type: string + format: date-time + description: Last Modified Timestamp + checksum: + type: string + description: Document checksum, changes when document file changes + documentId: + type: string + format: uuid + description: Document Identifier + contentType: + type: string + description: Document Content-Type + userId: + type: string + description: User who added document + contentLength: + type: integer + description: Document size + versionId: + type: string + description: Document version + belongsToDocumentId: + type: string + description: Parent Document Identifier + DocumentFulltextRequest: + required: + - query + type: object + description: Document full text search + properties: + query: + $ref: '#/components/schemas/DocumentFulltextSearch' + responseFields: + $ref: '#/components/schemas/SearchResponseFields' + DocumentFulltextSearch: + type: object + description: Document full text search criteria + properties: + page: + type: integer + description: Result page to return (starting at 1) + text: + type: string + description: Full text search + tags: + $ref: '#/components/schemas/DocumentFulltextTags' + DocumentFulltextTags: + type: array + description: List of search tags + items: + $ref: '#/components/schemas/DocumentFulltextTag' + DocumentFulltextTag: + required: + - key + type: object + properties: + eq: + type: string + description: Searches for strings that eq + eqOr: + type: array + description: Searches for ANY strings that eq + items: + type: string + key: + type: string + description: Tag key to search + DocumentSearchRequest: + required: + - query + type: object + description: Document search tag criteria + properties: + query: + $ref: '#/components/schemas/DocumentSearch' + responseFields: + $ref: '#/components/schemas/SearchResponseFields' + SearchResponseFields: + properties: + tags: + type: array + items: + type: string + DocumentSearchItemMeta: + type: object + properties: + folder: + type: string + description: Searches for a folder + path: + type: string + description: Searches for a Path of document + indexType: + type: string + description: Searches in an index + enum: + - folder + indexFilterBeginsWith: + type: string + description: Returns index records that begins with a particular substring + DocumentSearchItemTag: + required: + - key + type: object + properties: + beginsWith: + type: string + description: Searches for strings that begin with + eq: + type: string + description: Searches for strings that eq + eqOr: + type: array + description: Searches for ANY strings that eq + items: + type: string + key: + type: string + description: Tag key to search + AddWebhookRequest: + required: + - name + type: object + properties: + name: + type: string + description: Name of webhook + ttl: + type: string + description: Webhook time to live (expiry) + enabled: + type: string + description: Is webhook enabled + tags: + $ref: '#/components/schemas/AddDocumentTags' + AddDocumentUploadRequest: + required: + - content + type: object + properties: + tagSchemaId: + type: string + description: Tag Schema Id + path: + type: string + description: Path or Name of document + tags: + $ref: '#/components/schemas/AddDocumentTags' + actions: + $ref: '#/components/schemas/AddActions' + AddDocumentRequest: + required: + - content + type: object + properties: + tagSchemaId: + type: string + description: Tag Schema Id + path: + type: string + description: Path or Name of document + contentType: + type: string + description: Document media type + isBase64: + type: boolean + description: Is the content Base64-encoded? + content: + type: string + description: Document content + tags: + $ref: '#/components/schemas/AddDocumentTags' + metadata: + $ref: '#/components/schemas/AddDocumentMetadatas' + actions: + $ref: '#/components/schemas/AddActions' + documents: + $ref: '#/components/schemas/AddChildDocumentRequest' + AddDocumentTagsRequest: + type: object + description: Add List of document tags + properties: + tags: + $ref: '#/components/schemas/AddDocumentTags' + AddDocumentMetadatas: + type: array + description: List of document Metadata + items: + $ref: '#/components/schemas/AddDocumentMetadata' + AddDocumentMetadata: + required: + - key + type: object + description: Document Metadata (use either 'value' or 'values' not both) + properties: + key: + type: string + description: Metadata key + value: + type: string + description: Metadata value + values: + type: array + description: Metadata values + items: + type: string + description: Metadata value + AddDocumentTags: + type: array + description: List of document tags + items: + $ref: '#/components/schemas/AddDocumentTagRequest' + AddDocumentTagRequest: + required: + - key + type: object + description: List of Document Tags (use either 'value' or 'values' not both) + properties: + key: + type: string + description: Tag key + value: + type: string + description: Tag value + values: + type: array + description: Tag values + items: + type: string + description: Tag value + AddChildDocumentRequest: + type: array + description: List of child documents + items: + $ref: '#/components/schemas/AddChildDocument' + AddChildDocument: + required: + - content + type: object + description: List of related documents + properties: + path: + type: string + description: Path or Name of document + contentType: + type: string + description: Document Content-Type + isBase64: + type: boolean + description: Is the content Base64-encoded? + content: + type: string + description: Document content + DocumentItemVersion: + type: object + properties: + path: + type: string + description: Path or Name of document + insertedDate: + type: string + format: date-time + description: Inserted Timestamp + lastModifiedDate: + type: string + format: date-time + description: Last Modified Timestamp + checksum: + type: string + description: Document checksum, changes when document file changes + documentId: + type: string + format: uuid + description: Document Identifier + contentType: + type: string + description: Document Content-Type + userId: + type: string + description: User who added document + contentLength: + type: integer + description: Document size + versionId: + type: string + description: Document version + versionKey: + type: string + description: Document Version Identifier + GetDocumentTagResponse: + required: + - key + - value + type: object + properties: + insertedDate: + type: string + description: Inserted Timestamp + documentId: + type: string + description: Document Identifier + type: + type: string + description: Tag type + userId: + type: string + description: User who added document + value: + type: string + description: Tag value + values: + type: array + description: Tag values + items: + type: string + description: Tag value + key: + type: string + description: Tag key + QueryFulltextRequest: + description: OpenSearch search API request (https://opensearch.org/docs/latest/opensearch/rest-api/search/) + type: object + example: + query: + match: + title: Wind + QueryFulltextResponse: + type: object + properties: + result: + type: object + example: + result: + hits: + hits: + - _index: "" + _id: "" + _source: + documentId: "" + content: "" + DocumentFulltextResponse: + type: object + properties: + documents: + $ref: '#/components/schemas/FulltextSearchItems' + FulltextSearchItems: + type: array + description: List of search result documents + items: + $ref: '#/components/schemas/FulltextSearchItem' + FulltextSearchItem: + type: object + properties: + siteId: + type: string + description: Site Identifier + path: + type: string + description: Path or Name of document + insertedDate: + type: string + format: date-time + description: Inserted Timestamp + documentId: + type: string + format: uuid + description: Document Identifier + contentLength: + type: integer + description: Document size + createdBy: + type: string + description: User who added document + tags: + type: object + GetDocumentFulltextResponse: + type: object + properties: + siteId: + type: string + description: Site Identifier + content: + type: string + description: Content of document + path: + type: string + description: Path or Name of document + insertedDate: + type: string + format: date-time + description: Inserted Timestamp + documentId: + type: string + format: uuid + description: Document Identifier + createdBy: + type: string + description: User who added document + contentLength: + type: integer + description: Document size + tags: + type: object + metadata: + type: object + GetDocumentSyncResponse: + type: object + properties: + next: + type: string + description: Next page of results token + syncs: + $ref: '#/components/schemas/GetDocumentSyncs' + GetDocumentSyncs: + type: array + description: List of document syncs + items: + $ref: '#/components/schemas/GetDocumentSync' + GetDocumentSync: + type: object + properties: + service: + type: string + description: To which service the data was synced + enum: + - TYPESENSE + - OPENSEARCH + status: + type: string + description: The status of the sync + enum: + - COMPLETE + - FAILED + type: + type: string + description: The type of the sync + enum: + - METADATA + - TAG + syncDate: + type: string + format: date-time + description: Timestamp of synchronization + userId: + type: string + description: User who added document + message: + type: string + description: Document sync message + AddDocumentSyncRequest: + type: object + properties: + service: + type: string + description: To which service to sync the data to + enum: + - TYPESENSE + - OPENSEARCH + DocumentSearchResponse: + type: object + properties: + next: + type: string + description: Next page of results token + previous: + type: string + description: Previous page of results token + documents: + $ref: '#/components/schemas/SearchDocumentItems' + SearchDocumentItems: + type: array + description: List of search result documents + items: + $ref: '#/components/schemas/SearchDocumentItem' + SearchDocumentItem: + type: object + properties: + siteId: + type: string + description: Site Identifier + path: + type: string + description: Path or Name of document + insertedDate: + type: string + format: date-time + description: Inserted Timestamp + lastModifiedDate: + type: string + format: date-time + description: Last Modified Timestamp + folder: + type: boolean + description: Is Result a Document Folder + indexKey: + type: string + description: populated if search result are from an index + checksum: + type: string + description: Document checksum, changes when document file changes + documentId: + type: string + format: uuid + description: Document Identifier + contentType: + type: string + description: Document Content-Type + userId: + type: string + description: User who added document + contentLength: + type: integer + description: Document size + versionId: + type: string + description: Document version + matchedTag: + $ref: '#/components/schemas/DocumentSearchMatchTag' + tags: + type: object + DocumentSearchMatchTag: + type: object + properties: + key: + type: string + description: Tag key + value: + type: string + description: Tag value + type: + type: string + description: Tag type + GetDocumentsResponse: + type: object + properties: + next: + type: string + description: Next page of results token + previous: + type: string + description: Previous page of results token + documents: + $ref: '#/components/schemas/DocumentItemResults' + DocumentItemResults: + type: array + description: List of documents + items: + $ref: '#/components/schemas/DocumentItemResult' + DocumentItemResult: + type: object + properties: + siteId: + type: string + description: Site Identifier + path: + type: string + description: Path or Name of document + insertedDate: + type: string + format: date-time + description: Inserted Timestamp + lastModifiedDate: + type: string + format: date-time + description: Last Modified Timestamp + checksum: + type: string + description: Document checksum, changes when document file changes + documentId: + type: string + format: uuid + description: Document Identifier + contentType: + type: string + description: Document Content-Type + userId: + type: string + description: User who added document + contentLength: + type: integer + description: Document size + versionId: + type: string + description: Document version + GetWebhooksResponse: + type: object + properties: + webhooks: + $ref: '#/components/schemas/Webhooks' + Webhooks: + type: array + description: List of webhooks + items: + $ref: '#/components/schemas/GetWebhookResponse' + GetWebhookResponse: + type: object + properties: + siteId: + type: string + description: Site Identifier + name: + type: string + description: Webhook name + url: + type: string + description: Webhook url + insertedDate: + type: string + format: date-time + description: Inserted Timestamp + id: + type: string + format: uuid + description: Webhook Identifier + userId: + type: string + description: User who added document + GetWebhookTagsResponse: + type: object + properties: + next: + type: string + description: Next page of results token + previous: + type: string + description: Previous page of results token + tags: + $ref: '#/components/schemas/WebhookTagsList' + WebhookTagsList: + type: array + description: List of webhook tags + items: + $ref: '#/components/schemas/WebhookTag' + WebhookTag: + required: + - key + - value + type: object + properties: + insertedDate: + type: string + description: Inserted Timestamp + webhookId: + type: string + description: Webhook Identifier + type: + type: string + description: Tag type + userId: + type: string + description: User who added document + value: + type: string + description: Tag value + key: + type: string + description: Tag key + GetDocumentActionsResponse: + type: object + properties: + actions: + $ref: '#/components/schemas/DocumentActionList' + DocumentActionList: + type: array + description: List of document actions + items: + $ref: '#/components/schemas/DocumentAction' + DocumentAction: + type: object + properties: + status: + type: string + description: Status of the Document Action + type: + type: string + description: Type of Document Action + userId: + type: string + description: User who requested the Action + parameters: + type: object + description: Action parameters + GetDocumentOcrResponse: + type: object + properties: + data: + type: string + description: OCR text result + ocrEngine: + type: string + description: The OCR technique used + ocrStatus: + type: string + description: The status of the OCR request + contentType: + type: string + description: Document Content-Type + isBase64: + type: boolean + description: Is the content Base64-encoded? + userId: + type: string + description: User who requested the OCR + documentId: + type: string + format: uuid + description: Document Identifier + AddDocumentActionsResponse: + type: object + properties: + message: + type: string + description: Document Action message + AddDocumentOcrResponse: + type: object + properties: + message: + type: string + description: OCR processing message + SetDocumentFulltextResponse: + type: object + properties: + message: + type: string + description: Full text processing message + SetDocumentFulltextRequest: + type: object + properties: + contentType: + type: string + description: Document Content-Type + content: + type: string + description: Document content + contentUrls: + type: array + items: + type: string + description: URL(s) which contain document content + path: + type: string + description: Path or Name of document + tags: + $ref: '#/components/schemas/AddDocumentTags' + metadata: + $ref: '#/components/schemas/AddDocumentMetadatas' + UpdateDocumentFulltextRequest: + type: object + properties: + path: + type: string + description: Path or Name of document + content: + type: string + description: Document content + tags: + $ref: '#/components/schemas/AddDocumentTags' + SetAntivirusRequest: + type: object + SetAntivirusResponse: + type: object + properties: + message: + type: string + description: Antivirus processing message + AddDocumentActionsRequest: + type: object + properties: + actions: + $ref: '#/components/schemas/AddActions' + AddActions: + type: array + description: List of Actions + items: + $ref: '#/components/schemas/AddAction' + AddAction: + type: object + required: + - type + properties: + type: + type: string + description: Type of Document Action + enum: + - OCR + - FULLTEXT + - ANTIVIRUS + - WEBHOOK + - DOCUMENTTAGGING + parameters: + $ref: '#/components/schemas/AddActionParameters' + AddActionParameters: + type: object + properties: + ocrParseTypes: + type: string + description: 'OCR: Parse types - TEXT, FORMS, TABLES' + addPdfDetectedCharactersAsText: + type: boolean + description: 'OCR: For the rewriting of the PDF document, converting any image text to searchable text' + url: + type: string + description: 'Webhook: Callback URL' + characterMax: + type: string + description: 'Fulltext: Maximum number of characters (-1 unlimited, Typesense defaults to 2048 characters)' + engine: + type: string + description: 'DocumentTagging: Engine to use for document tagging generation' + enum: + - chatgpt + tags: + description: 'DocumentTagging: List of Tags to generate tags for' + items: + type: string + AddDocumentOcrRequest: + type: object + example: + parseTypes: + - TEXT + - FORMS + - TABLES + properties: + parseTypes: + type: array + description: OCR Parse types - TEXT, FORMS, TABLES + items: + type: string + addPdfDetectedCharactersAsText: + type: boolean + description: Rewrite PDF document, converting any Image text to searchable text + SetDocumentOcrRequest: + required: + - content + type: object + properties: + contentType: + type: string + description: Document Content-Type + isBase64: + type: boolean + description: Is the content Base64-encoded? + content: + type: string + description: Document content + GetVersionRequest: + type: object + properties: + version: + type: string + description: FormKiQ version + type: + type: string + description: FormKiQ type + modules: + type: array + description: List of installed modules + items: + type: string + SetConfigRequest: + type: object + properties: + chatGptApiKey: + type: string + description: ChatGPT Api Key + maxContentLengthBytes: + type: string + description: Set Maximum Document Content Length in Bytes + maxDocuments: + type: string + description: Set Maximum number of Documents allowed + maxWebhooks: + type: string + description: Set Maximum number of Webhooks allowed + SetConfigResponse: + type: object + properties: + message: + type: string + description: Result message + AddApiKeyRequest: + type: object + properties: + name: + type: string + description: Name of API Key + AddApiKeyResponse: + type: object + properties: + message: + type: string + description: Result message + DeleteApiKeyResponse: + type: object + properties: + message: + type: string + description: Result message + GetApiKeysResponse: + type: object + properties: + apiKeys: + $ref: '#/components/schemas/ApiKeys' + ApiKeys: + type: array + description: List of ApiKeys + items: + $ref: '#/components/schemas/ApiKey' + ApiKey: + type: object + properties: + name: + type: string + description: Name of API Key + apiKey: + type: string + description: API Key value + userId: + type: string + siteIds: + type: array + items: + type: string + insertedDate: + type: string + format: date-time + description: Inserted Timestamp + GetConfigurationResponse: + type: object + properties: + chatGptApiKey: + type: string + description: ChatGPT API Key + maxContentLengthBytes: + type: string + description: Set Maximum Document Content Length in Bytes + maxDocuments: + type: string + description: Set Maximum number of Documents allowed + maxWebhooks: + type: string + description: Set Maximum number of Webhooks allowed + GetSitesRequest: + type: object + properties: + sites: + $ref: '#/components/schemas/Sites' + Sites: + type: array + description: List of sites + items: + $ref: '#/components/schemas/Site' + Site: + type: object + properties: + siteId: + type: string + description: Site Identifier + permission: + type: string + description: SiteId permission level + enum: + - READ_WRITE + - READ_ONLY + uploadEmail: + type: string + description: SiteId document upload email address + TagSchemaPostResponse: + type: object + properties: + tagSchemaId: + type: string + format: uuid + description: Tag Schema Identifier + ErrorsResponse: + type: object + properties: + errors: + $ref: '#/components/schemas/ErrorsList' + ErrorsList: + type: array + description: List of errors + items: + $ref: '#/components/schemas/Error' + Error: + type: object + properties: + key: + type: string + description: Error Key + error: + type: string + description: Error Message + GetTagSchemasRequest: + type: object + properties: + schemas: + $ref: '#/components/schemas/TagSchemas' + next: + type: string + previous: + type: string + TagSchemas: + type: array + description: List of Tag Schemas + items: + $ref: '#/components/schemas/TagSchemaSummary' + AddTagSchemaRequest: + type: object + properties: + name: + type: string + tags: + $ref: '#/components/schemas/AddTagSchemaTags' + AddTagSchemaTags: + type: object + properties: + compositeKeys: + type: array + description: List of Composite Keys + items: + $ref: '#/components/schemas/TagSchemaCompositeKey' + required: + type: array + description: List of Required Tags + items: + $ref: '#/components/schemas/TagSchemaRequired' + optional: + type: array + description: List of Optional Tags + items: + $ref: '#/components/schemas/TagSchemaOptional' + allowAdditionalTags: + type: boolean + default: true + TagSchemaSummary: + type: object + properties: + tagSchemaId: + type: string + name: + type: string + userId: + type: string + insertedDate: + type: string + format: date-time + description: Inserted Timestamp + GetTagSchemaRequest: + type: object + properties: + tagSchemaId: + type: string + name: + type: string + userId: + type: string + insertedDate: + type: string + format: date-time + description: Inserted Timestamp + tags: + type: object + properties: + compositeKeys: + type: array + description: List of Composite Keys + items: + $ref: '#/components/schemas/TagSchemaCompositeKey' + required: + type: array + description: List of Required Tags + items: + $ref: '#/components/schemas/TagSchemaRequired' + optional: + type: array + description: List of Optional Tags + items: + $ref: '#/components/schemas/TagSchemaOptional' + allowAdditionalTags: + type: boolean + default: true + TagSchemaCompositeKey: + type: object + properties: + key: + type: array + items: + type: string + TagSchemaRequired: + type: object + properties: + key: + type: string + defaultValues: + type: array + description: Default values + items: + type: string + allowedValues: + type: array + description: Only valid values + items: + type: string + TagSchemaOptional: + type: object + properties: + key: + type: string + defaultValues: + type: array + description: Default values + items: + type: string + allowedValues: + type: array + description: Only valid values + items: + type: string + OnlyOfficeDocumentNewRequest: + description: ONLYOFFICE New Document Request + properties: + extension: + type: string + enum: + - DOCX + - XLSX + - PPTX + OnlyOfficeDocumentEditRequest: + description: ONLYOFFICE Edit Document Request + OnlyOfficeDocumentResponse: + type: object + properties: + config: + type: object + properties: + onlyOfficeUrl: + description: URL of the ONLYOFFICE application + type: string + token: + description: ONLYOFFICE security token + type: string + documentType: + description: Type of document (https://api.onlyoffice.com/editors/config/) + type: string + editorConfig: + type: object + properties: + callbackUrl: + type: string + document: + type: object + properties: + url: + description: Defines the absolute URL where the source viewed or edited document is stored + type: string + title: + description: Defines the desired file name for the viewed or edited document + type: string + key: + description: Defines the unique document identifier used by the service to recognize the document + type: string + fileType: + description: Defines the type of the file for the source viewed or edited document + type: string + OnlyOfficeDocumentSaveResponse: + type: object + properties: + message: + type: string + EsignatureDocusignRequest: + type: object + properties: + emailSubject: + type: string + description: Email Subject + status: + type: string + description: The status of the request + enum: + - created + - sent + developmentMode: + type: boolean + description: Whether to enable developer mode + signers: + $ref: '#/components/schemas/EsignatureDocusignSigners' + carbonCopies: + $ref: '#/components/schemas/EsignatureDocusignCarbonCopies' + recipientTabs: + $ref: '#/components/schemas/EsignatureDocusignRecipientTabs' + EsignatureDocusignSigners: + type: array + description: List of DocuSign Signers + items: + $ref: '#/components/schemas/EsignatureDocusignSigner' + EsignatureDocusignSigner: + type: object + properties: + name: + type: string + description: Name of Signer + email: + type: string + description: Email of Signer + EsignatureDocusignCarbonCopies: + type: array + description: List of DocuSign Carbon Copies + items: + $ref: '#/components/schemas/EsignatureDocusignCarbonCopy' + EsignatureDocusignCarbonCopy: + type: object + properties: + name: + type: string + description: Name of Signer + email: + type: string + description: Email of Signer + EsignatureDocusignRecipientTabs: + type: array + description: List of DocuSign Recipient Tabs + items: + $ref: '#/components/schemas/EsignatureDocusignRecipientTab' + EsignatureDocusignRecipientTab: + type: object + properties: + type: + type: string + description: Type of Recipient + enum: + - signHere + pageNumber: + type: string + description: Document Page Number + positionX: + type: string + description: Element X Position + positionY: + type: string + description: Element Y Position + EsignatureDocusignResponse: + type: object + properties: + redirect_url: + type: string + description: Redirect Url to complete DocuSign workflow + message: + type: string + description: Result message + EsignatureDocusignConfigResponse: + type: object + properties: + configured: + type: boolean + description: Whether DocuSign is configured + userId: + type: string + description: DocuSign UserId configured + clientId: + type: string + description: DocuSign Client configured + EsignatureSetDocusignConfigRequest: + type: object + required: + - privateKey + - userId + - clientId + properties: + privateKey: + type: string + description: DocuSign App RSA PRIVATE KEY + userId: + type: string + description: DocuSign App UserId + clientId: + type: string + description: DocuSign App Client + EsignatureSetDocusignConfigResponse: + type: object + properties: + message: + type: string + description: Message response + securitySchemes: + ApiAuthorization: + type: apiKey + name: Authorization + in: header + x-amazon-apigateway-authtype: Custom API Key + x-amazon-apigateway-authorizer: + identitySource: $request.header.Authorization + authorizerUri: + Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ApiKeyAuthorizer.Arn}/invocations + authorizerResultTtlInSeconds: 300 + authorizerPayloadFormatVersion: "2.0" + enableSimpleResponses: true + type: request + x-amazon-apigateway-integrations: + lambdaApi200: + uri: + Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${DocumentsApiRequests.Arn}/invocations + responses: + default: + statusCode: "200" + responseParameters: + method.response.header.Access-Control-Allow-Methods: '''*''' + method.response.header.Access-Control-Allow-Headers: '''Content-Type,X-Amz-Date,Authorization,X-Api-Key''' + method.response.header.Access-Control-Allow-Origin: '''*''' + passthroughBehavior: when_no_templates + httpMethod: POST + type: aws_proxy + payloadFormatVersion: "1.0" + ocrLambdaApi200: + uri: + Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${DocumentsApiRequests.Arn}/invocations + responses: + default: + statusCode: "200" + responseParameters: + method.response.header.Access-Control-Allow-Methods: '''*''' + method.response.header.Access-Control-Allow-Headers: '''Content-Type,X-Amz-Date,Authorization,X-Api-Key''' + method.response.header.Access-Control-Allow-Origin: '''*''' + passthroughBehavior: when_no_templates + httpMethod: POST + type: aws_proxy + payloadFormatVersion: "1.0" + antivirusLambdaApi200: + uri: + Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${DocumentsApiRequests.Arn}/invocations + responses: + default: + statusCode: "200" + responseParameters: + method.response.header.Access-Control-Allow-Methods: '''*''' + method.response.header.Access-Control-Allow-Headers: '''Content-Type,X-Amz-Date,Authorization,X-Api-Key''' + method.response.header.Access-Control-Allow-Origin: '''*''' + passthroughBehavior: when_no_templates + httpMethod: POST + type: aws_proxy + payloadFormatVersion: "1.0" + searchLambdaApi200: + uri: + Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${DocumentsApiRequests.Arn}/invocations + responses: + default: + statusCode: "200" + responseParameters: + method.response.header.Access-Control-Allow-Methods: '''*''' + method.response.header.Access-Control-Allow-Headers: '''Content-Type,X-Amz-Date,Authorization,X-Api-Key''' + method.response.header.Access-Control-Allow-Origin: '''*''' + passthroughBehavior: when_no_templates + httpMethod: POST + type: aws_proxy + payloadFormatVersion: "1.0" + fulltextLambdaApi200: + uri: + Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${DocumentsApiRequests.Arn}/invocations + responses: + default: + statusCode: "200" + responseParameters: + method.response.header.Access-Control-Allow-Methods: '''*''' + method.response.header.Access-Control-Allow-Headers: '''Content-Type,X-Amz-Date,Authorization,X-Api-Key''' + method.response.header.Access-Control-Allow-Origin: '''*''' + passthroughBehavior: when_no_templates + httpMethod: POST + type: aws_proxy + payloadFormatVersion: "1.0" + onlyOfficeLambdaApi200: + uri: + Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${DocumentsApiRequests.Arn}/invocations + responses: + default: + statusCode: "200" + responseParameters: + method.response.header.Access-Control-Allow-Methods: '''*''' + method.response.header.Access-Control-Allow-Headers: '''Content-Type,X-Amz-Date,Authorization,X-Api-Key''' + method.response.header.Access-Control-Allow-Origin: '''*''' + passthroughBehavior: when_no_templates + httpMethod: POST + type: aws_proxy + payloadFormatVersion: "1.0" + esignatureLambdaApi200: + uri: + Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${DocumentsApiRequests.Arn}/invocations + responses: + default: + statusCode: "200" + responseParameters: + method.response.header.Access-Control-Allow-Methods: '''*''' + method.response.header.Access-Control-Allow-Headers: '''Content-Type,X-Amz-Date,Authorization,X-Api-Key''' + method.response.header.Access-Control-Allow-Origin: '''*''' + passthroughBehavior: when_no_templates + httpMethod: POST + type: aws_proxy + payloadFormatVersion: "1.0" + lambdaApi201: + uri: + Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${DocumentsApiRequests.Arn}/invocations + responses: + default: + statusCode: "201" + responseParameters: + method.response.header.Access-Control-Allow-Headers: '''Content-Type,X-Amz-Date,Authorization,X-Api-Key''' + method.response.header.Access-Control-Allow-Methods: '''*''' + method.response.header.Access-Control-Allow-Origin: '''*''' + passthroughBehavior: WHEN_NO_TEMPLATES + httpMethod: POST + type: aws_proxy + payloadFormatVersion: "1.0" + x-amazon-apigateway-gateway-responses: + UNAUTHORIZED: + statusCode: 401 + responseParameters: + gatewayresponse.header.Access-Control-Allow-Methods: '''*''' + gatewayresponse.header.Access-Control-Allow-Origin: '''*''' + gatewayresponse.header.Access-Control-Allow-Headers: '''Content-Type,X-Amz-Date,Authorization,X-Api-Key''' + x-amazon-apigateway-cors: + allowOrigins: + - '*' + maxAge: 3600 + allowMethods: + - '*' + allowHeaders: + - '*' \ No newline at end of file diff --git a/document-events/build.gradle b/document-events/build.gradle index 4a48f0d92..664ea3ac4 100644 --- a/document-events/build.gradle +++ b/document-events/build.gradle @@ -4,8 +4,8 @@ description = "FormKiQ Document Events" dependencies { implementation project(':aws-sns') - implementation group: 'com.formkiq', name: 'graalvm-annotations', version: '1.1.0' - implementation group: 'com.google.code.gson', name: 'gson', version: '2.10' + implementation group: 'com.formkiq', name: 'graalvm-annotations', version: '1.2.0' + implementation group: 'com.google.code.gson', name: 'gson', version: '2.10.1' testImplementation project(':fkq-test-utils') testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version:'5.9.1' @@ -13,6 +13,5 @@ dependencies { test { failFast = true - useJUnitPlatform() - exclude 'com/formkiq/module/actions/awstest/**' + useJUnitPlatform() } diff --git a/dynamodb-documents/.classpath b/dynamodb-documents/.classpath index 66f8050c0..58920f4a9 100644 --- a/dynamodb-documents/.classpath +++ b/dynamodb-documents/.classpath @@ -3,29 +3,41 @@ - + - + - + - + + + + + + + + + + + + + diff --git a/dynamodb-documents/build.gradle b/dynamodb-documents/build.gradle index 5309ee2fe..5be935be8 100644 --- a/dynamodb-documents/build.gradle +++ b/dynamodb-documents/build.gradle @@ -1,25 +1,38 @@ description = "DocumentStore Dynamodb" +sourceSets { + integration { + java.srcDir "$projectDir/src/integration/java" + resources.srcDir "$projectDir/src/integration/resources" + compileClasspath += main.output + test.output + runtimeClasspath += main.output + test.output + } +} + +configurations { + integrationImplementation.extendsFrom testImplementation + integrationRuntime.extendsFrom testRuntime +} + dependencies { implementation project(':aws-dynamodb') implementation project(':fkq-plugins') implementation project(':fkq-validation') implementation project(':fkq-lambda-services') - implementation group: 'com.formkiq', name: 'graalvm-annotations', version: '1.1.0' + implementation group: 'com.formkiq', name: 'graalvm-annotations', version: '1.2.0' testImplementation project(':aws-ssm') testImplementation project(':fkq-test-utils') - testImplementation 'org.slf4j:slf4j-api:2.0.6' + testImplementation 'org.slf4j:slf4j-api:2.0.7' testImplementation group: 'com.google.guava', name: 'guava', version: '31.1-jre' - testImplementation group: 'org.slf4j', name: 'slf4j-simple', version: '2.0.6' + testImplementation group: 'org.slf4j', name: 'slf4j-simple', version: '2.0.7' testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version:'5.9.1' - testImplementation group: 'org.junit.vintage', name: 'junit-vintage-engine', version:'5.9.1' testImplementation group: 'org.testcontainers', name: 'testcontainers', version: '1.17.6' testImplementation group: 'org.testcontainers', name: 'junit-jupiter', version: '1.17.6' - testImplementation group: 'com.amazonaws', name: 'aws-java-sdk', version: '1.12.370' + testImplementation group: 'com.amazonaws', name: 'aws-java-sdk', version: '1.12.436' configurations.all { exclude group: 'software.amazon.awssdk', module: 'apache-client' @@ -44,12 +57,11 @@ build.dependsOn createZip test { failFast = true - useJUnitPlatform() - exclude 'com/formkiq/stacks/dynamodb/awstest/**' + useJUnitPlatform() } -task testaws(type: Test) { - outputs.upToDateWhen {false} - description = 'Runs AWS integration tests.' - include 'com/formkiq/stacks/dynamodb/awstest/**' -} \ No newline at end of file +task integrationTest(type: Test) { + testClassesDirs = sourceSets.integration.output.classesDirs + classpath = sourceSets.integration.runtimeClasspath + useJUnitPlatform() +} diff --git a/dynamodb-documents/config/checkstyle/import-control.xml b/dynamodb-documents/config/checkstyle/import-control.xml index a4bda69c6..a8ce37531 100644 --- a/dynamodb-documents/config/checkstyle/import-control.xml +++ b/dynamodb-documents/config/checkstyle/import-control.xml @@ -28,6 +28,7 @@ + diff --git a/dynamodb-documents/documentsTableSchema.numbers b/dynamodb-documents/documentsTableSchema.numbers index 18fe9e11e..ba28c3aca 100755 Binary files a/dynamodb-documents/documentsTableSchema.numbers and b/dynamodb-documents/documentsTableSchema.numbers differ diff --git a/dynamodb-documents/src/test/java/com/formkiq/stacks/dynamodb/awstest/AwsResourceTest.java b/dynamodb-documents/src/integration/java/com/formkiq/stacks/dynamodb/awstest/AwsResourceTest.java similarity index 89% rename from dynamodb-documents/src/test/java/com/formkiq/stacks/dynamodb/awstest/AwsResourceTest.java rename to dynamodb-documents/src/integration/java/com/formkiq/stacks/dynamodb/awstest/AwsResourceTest.java index 07cb205b0..66872eb04 100644 --- a/dynamodb-documents/src/test/java/com/formkiq/stacks/dynamodb/awstest/AwsResourceTest.java +++ b/dynamodb-documents/src/integration/java/com/formkiq/stacks/dynamodb/awstest/AwsResourceTest.java @@ -23,11 +23,11 @@ */ package com.formkiq.stacks.dynamodb.awstest; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import com.formkiq.aws.dynamodb.DynamoDbConnectionBuilder; import com.formkiq.aws.ssm.SsmConnectionBuilder; import com.formkiq.aws.ssm.SsmService; @@ -56,7 +56,7 @@ public class AwsResourceTest { * * @throws IOException IOException */ - @BeforeClass + @BeforeAll public static void beforeClass() throws IOException { String awsprofile = System.getProperty("testprofile"); @@ -64,11 +64,11 @@ public static void beforeClass() throws IOException { awsregion = Region.of(System.getProperty("testregion")); final SsmConnectionBuilder ssmBuilder = - new SsmConnectionBuilder().setCredentials(awsprofile).setRegion(awsregion); + new SsmConnectionBuilder(false).setCredentials(awsprofile).setRegion(awsregion); ssmService = new SsmServiceImpl(ssmBuilder); - dynamoDB = - new DynamoDbConnectionBuilder().setCredentials(awsprofile).setRegion(awsregion).build(); + dynamoDB = new DynamoDbConnectionBuilder(false).setCredentials(awsprofile).setRegion(awsregion) + .build(); } /** diff --git a/dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/ApiKeysService.java b/dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/ApiKeysService.java new file mode 100644 index 000000000..8621f9084 --- /dev/null +++ b/dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/ApiKeysService.java @@ -0,0 +1,75 @@ +/** + * MIT License + * + * Copyright (c) 2018 - 2020 FormKiQ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.formkiq.stacks.dynamodb; + +import java.util.List; +import com.formkiq.aws.dynamodb.DynamicObject; + +/** + * + * API Key Service. + * + */ +public interface ApiKeysService { + + /** + * Create API Key. + * + * @param siteId {@link String} + * @param name {@link String} + * @param userId {@link String} + * @return {@link String} + */ + String createApiKey(String siteId, String name, String userId); + + /** + * Delete Api Key. + * + * @param apiKey {@link String} + */ + void deleteApiKey(String apiKey); + + /** + * Get Api Key. + * + * @param apiKey {@link String} + * @return boolean + */ + DynamicObject get(String apiKey); + + /** + * Get List of API Keys. + * + * @return {@link List} {@link String} + */ + List list(); + + /** + * Mask Api Key. + * + * @param apiKey {@link String} + * @return {@link String} + */ + String mask(String apiKey); +} diff --git a/dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/ApiKeysServiceDynamoDb.java b/dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/ApiKeysServiceDynamoDb.java new file mode 100644 index 000000000..0ab77cdbf --- /dev/null +++ b/dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/ApiKeysServiceDynamoDb.java @@ -0,0 +1,167 @@ +/** + * MIT License + * + * Copyright (c) 2018 - 2020 FormKiQ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.formkiq.stacks.dynamodb; + +import static com.formkiq.aws.dynamodb.SiteIdKeyGenerator.DEFAULT_SITE_ID; +import java.security.SecureRandom; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import com.formkiq.aws.dynamodb.AttributeValueToDynamicObject; +import com.formkiq.aws.dynamodb.DbKeys; +import com.formkiq.aws.dynamodb.DynamicObject; +import com.formkiq.aws.dynamodb.DynamoDbConnectionBuilder; +import com.formkiq.aws.dynamodb.DynamoDbService; +import com.formkiq.aws.dynamodb.DynamoDbServiceImpl; +import com.formkiq.aws.dynamodb.QueryConfig; +import com.formkiq.aws.dynamodb.objects.DateUtil; +import software.amazon.awssdk.services.dynamodb.model.AttributeValue; +import software.amazon.awssdk.services.dynamodb.model.QueryResponse; + +/** + * + * DynamoDb implementation of {@link ApiKeysService}. + * + */ +public class ApiKeysServiceDynamoDb implements ApiKeysService, DbKeys { + + /** Api Key Length. */ + private static final int API_KEY_LENGTH = 51; + /** API Query Limit. */ + private static final int LIMIT = 100; + /** Mask Value, must be even number. */ + private static final int MASK = 8; + + /** + * Generate Random String. + * + * @param len int + * @return {@link String} + */ + private static String generateRandomString(final int len) { + final String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"; + SecureRandom random = new SecureRandom(); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < len; i++) { + int randomIndex = random.nextInt(chars.length()); + sb.append(chars.charAt(randomIndex)); + } + + return sb.toString(); + } + + /** {@link DynamoDbService}. */ + private DynamoDbService db; + + /** {@link SimpleDateFormat} in ISO Standard format. */ + private SimpleDateFormat df = DateUtil.getIsoDateFormatter(); + + /** + * constructor. + * + * @param connection {@link DynamoDbConnectionBuilder} + * @param documentsTable {@link String} + */ + public ApiKeysServiceDynamoDb(final DynamoDbConnectionBuilder connection, + final String documentsTable) { + if (documentsTable == null) { + throw new IllegalArgumentException("Table name is null"); + } + + this.db = new DynamoDbServiceImpl(connection, documentsTable); + } + + @Override + public String createApiKey(final String siteId, final String name, final String userId) { + + String site = siteId != null ? siteId : DEFAULT_SITE_ID; + String apiKey = generateRandomString(API_KEY_LENGTH); + Map keys = getKeys(apiKey); + keys.put("apiKey", AttributeValue.fromS(apiKey)); + keys.put("name", AttributeValue.fromS(name)); + keys.put("siteIds", AttributeValue.fromL(Arrays.asList(AttributeValue.fromS(site)))); + keys.put("insertedDate", AttributeValue.fromS(this.df.format(new Date()))); + keys.put("userId", AttributeValue.fromS(userId)); + this.db.putItem(keys); + return apiKey; + } + + @Override + public void deleteApiKey(final String apiKey) { + + Map keys = getKeys(apiKey); + + QueryConfig config = new QueryConfig().projectionExpression("PK,SK"); + + String apiKeyStart = apiKey.substring(0, MASK); + String apiKeyEnd = apiKey.substring(apiKey.length() - MASK / 2); + + QueryResponse response = this.db.queryBeginsWith(config, keys.get(PK), + AttributeValue.fromS(PREFIX_API_KEY + apiKeyStart), null, LIMIT); + + response.items().forEach(i -> { + if (i.get(SK).s().endsWith(apiKeyEnd)) { + this.db.deleteItem(i.get(PK), i.get(SK)); + } + }); + + } + + @Override + public DynamicObject get(final String apiKey) { + Map keys = getKeys(apiKey); + Map map = this.db.get(keys.get(PK), keys.get(SK)); + return new AttributeValueToDynamicObject().apply(map); + } + + private Map getKeys(final String apiKey) { + return keysGeneric(null, PREFIX_API_KEYS, PREFIX_API_KEY + mask(apiKey)); + } + + @Override + public List list() { + Map keys = getKeys(""); + QueryResponse response = this.db.query(keys.get(PK), null, LIMIT); + + List list = + response.items().stream().map(new AttributeValueToDynamicObject()).map(o -> { + String apiKey = mask(o.getString("apiKey")); + o.put("apiKey", apiKey); + o.remove(PK); + o.remove(SK); + return o; + }).collect(Collectors.toList()); + + return list; + } + + @Override + public String mask(final String apiKey) { + return apiKey != null && apiKey.length() == API_KEY_LENGTH ? apiKey.subSequence(0, MASK) + + "****************" + apiKey.substring(apiKey.length() - MASK / 2) : apiKey; + } +} diff --git a/dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/ApiKeysServiceExtension.java b/dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/ApiKeysServiceExtension.java new file mode 100644 index 000000000..3e6b4ce24 --- /dev/null +++ b/dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/ApiKeysServiceExtension.java @@ -0,0 +1,56 @@ +/** + * MIT License + * + * Copyright (c) 2018 - 2020 FormKiQ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.formkiq.stacks.dynamodb; + +import com.formkiq.aws.dynamodb.DynamoDbConnectionBuilder; +import com.formkiq.module.lambdaservices.AwsServiceCache; +import com.formkiq.module.lambdaservices.AwsServiceExtension; + +/** + * + * {@link AwsServiceExtension} for {@link ApiKeysService}. + * + */ +public class ApiKeysServiceExtension implements AwsServiceExtension { + + /** {@link ApiKeysService}. */ + private ApiKeysService service; + + /** + * constructor. + */ + public ApiKeysServiceExtension() {} + + @Override + public ApiKeysService loadService(final AwsServiceCache awsServiceCache) { + if (this.service == null) { + DynamoDbConnectionBuilder connection = + awsServiceCache.getExtension(DynamoDbConnectionBuilder.class); + this.service = + new ApiKeysServiceDynamoDb(connection, awsServiceCache.environment("DOCUMENTS_TABLE")); + } + + return this.service; + } +} diff --git a/fkq-lambda-core/src/main/java/com/formkiq/aws/services/lambda/services/ConfigService.java b/dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/ConfigService.java similarity index 95% rename from fkq-lambda-core/src/main/java/com/formkiq/aws/services/lambda/services/ConfigService.java rename to dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/ConfigService.java index b7fedd07b..54d049128 100644 --- a/fkq-lambda-core/src/main/java/com/formkiq/aws/services/lambda/services/ConfigService.java +++ b/dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/ConfigService.java @@ -21,21 +21,23 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package com.formkiq.aws.services.lambda.services; +package com.formkiq.stacks.dynamodb; import com.formkiq.aws.dynamodb.DynamicObject; /** Config Service. */ public interface ConfigService { + /** ChatGpt Api Key. */ + String CHATGPT_API_KEY = "ChatGptApiKey"; /** Document Time To Live Key. */ String DOCUMENT_TIME_TO_LIVE = "DocumentTimeToLive"; - /** Max Webhooks Key. */ - String MAX_WEBHOOKS = "MaxWebhooks"; - /** Max Documents Key. */ - String MAX_DOCUMENTS = "MaxDocuments"; /** Max Document Size. */ String MAX_DOCUMENT_SIZE_BYTES = "MaxContentLengthBytes"; + /** Max Documents Key. */ + String MAX_DOCUMENTS = "MaxDocuments"; + /** Max Webhooks Key. */ + String MAX_WEBHOOKS = "MaxWebhooks"; /** Webhook Time To Live Key. */ String WEBHOOK_TIME_TO_LIVE = "WebhookTimeToLive"; diff --git a/fkq-lambda-core/src/main/java/com/formkiq/aws/services/lambda/services/ConfigServiceImpl.java b/dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/ConfigServiceDynamoDb.java similarity index 70% rename from fkq-lambda-core/src/main/java/com/formkiq/aws/services/lambda/services/ConfigServiceImpl.java rename to dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/ConfigServiceDynamoDb.java index 077e7f296..b348e8861 100644 --- a/fkq-lambda-core/src/main/java/com/formkiq/aws/services/lambda/services/ConfigServiceImpl.java +++ b/dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/ConfigServiceDynamoDb.java @@ -21,12 +21,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package com.formkiq.aws.services.lambda.services; +package com.formkiq.stacks.dynamodb; import static com.formkiq.aws.dynamodb.SiteIdKeyGenerator.DEFAULT_SITE_ID; import static com.formkiq.aws.dynamodb.SiteIdKeyGenerator.isDefaultSiteId; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -35,21 +36,15 @@ import com.formkiq.aws.dynamodb.DbKeys; import com.formkiq.aws.dynamodb.DynamicObject; import com.formkiq.aws.dynamodb.DynamoDbConnectionBuilder; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; +import com.formkiq.aws.dynamodb.DynamoDbService; +import com.formkiq.aws.dynamodb.DynamoDbServiceImpl; import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.BatchGetItemRequest; -import software.amazon.awssdk.services.dynamodb.model.BatchGetItemResponse; -import software.amazon.awssdk.services.dynamodb.model.DeleteItemRequest; -import software.amazon.awssdk.services.dynamodb.model.KeysAndAttributes; -import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; /** Implementation of the {@link ConfigService}. */ -public class ConfigServiceImpl implements ConfigService, DbKeys { +public class ConfigServiceDynamoDb implements ConfigService, DbKeys { - /** {@link DynamoDbClient}. */ - private DynamoDbClient dbClient; - /** Documents Table Name. */ - private String documentTableName; + /** {@link DynamoDbService}. */ + private DynamoDbService db; /** * constructor. @@ -57,22 +52,20 @@ public class ConfigServiceImpl implements ConfigService, DbKeys { * @param connection {@link DynamoDbConnectionBuilder} * @param documentsTable {@link String} */ - public ConfigServiceImpl(final DynamoDbConnectionBuilder connection, + public ConfigServiceDynamoDb(final DynamoDbConnectionBuilder connection, final String documentsTable) { if (documentsTable == null) { throw new IllegalArgumentException("Table name is null"); } - this.dbClient = connection.build(); - this.documentTableName = documentsTable; + this.db = new DynamoDbServiceImpl(connection, documentsTable); } @Override public void delete(final String siteId) { String s = siteId != null ? siteId : DEFAULT_SITE_ID; Map keys = keysGeneric(null, PREFIX_CONFIG, s); - this.dbClient.deleteItem( - DeleteItemRequest.builder().tableName(this.documentTableName).key(keys).build()); + this.db.deleteItem(keys.get(PK), keys.get(SK)); } @Override @@ -87,17 +80,9 @@ public DynamicObject get(final String siteId) { keys.add(keysGeneric(null, PREFIX_CONFIG, DEFAULT_SITE_ID)); } - Map items = - Map.of(this.documentTableName, KeysAndAttributes.builder().keys(keys).build()); - - BatchGetItemResponse response = - this.dbClient.batchGetItem(BatchGetItemRequest.builder().requestItems(items).build()); - - AttributeValueToDynamicObject transform = new AttributeValueToDynamicObject(); + List> list = this.db.getBatch(keys); Optional> map = Optional.empty(); - List> list = response.responses().get(this.documentTableName); - if (!list.isEmpty()) { map = list.stream().filter(s -> s.get(SK).s().equals(siteId)).findFirst(); if (map.isEmpty()) { @@ -105,6 +90,7 @@ public DynamicObject get(final String siteId) { } } + AttributeValueToDynamicObject transform = new AttributeValueToDynamicObject(); return !map.isEmpty() ? transform.apply(map.get()) : new DynamicObject(Map.of()); } @@ -117,7 +103,14 @@ public void save(final String siteId, final DynamicObject obj) { item.put(e.getKey(), AttributeValue.builder().s(e.getValue().toString()).build()); } - this.dbClient - .putItem(PutItemRequest.builder().tableName(this.documentTableName).item(item).build()); + if (this.db.exists(item.get(PK), item.get(SK))) { + HashMap fields = new HashMap<>(item); + fields.remove(PK); + fields.remove(SK); + + this.db.updateFields(item.get(PK), item.get(SK), fields); + } else { + this.db.putItem(item); + } } } diff --git a/fkq-lambda-core/src/main/java/com/formkiq/aws/services/lambda/services/ConfigServiceExtension.java b/dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/ConfigServiceExtension.java similarity index 93% rename from fkq-lambda-core/src/main/java/com/formkiq/aws/services/lambda/services/ConfigServiceExtension.java rename to dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/ConfigServiceExtension.java index 6ccc80dbc..db6689d70 100644 --- a/fkq-lambda-core/src/main/java/com/formkiq/aws/services/lambda/services/ConfigServiceExtension.java +++ b/dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/ConfigServiceExtension.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package com.formkiq.aws.services.lambda.services; +package com.formkiq.stacks.dynamodb; import com.formkiq.aws.dynamodb.DynamoDbConnectionBuilder; import com.formkiq.module.lambdaservices.AwsServiceCache; @@ -48,7 +48,7 @@ public ConfigService loadService(final AwsServiceCache awsServiceCache) { DynamoDbConnectionBuilder connection = awsServiceCache.getExtension(DynamoDbConnectionBuilder.class); this.service = - new ConfigServiceImpl(connection, awsServiceCache.environment("DOCUMENTS_TABLE")); + new ConfigServiceDynamoDb(connection, awsServiceCache.environment("DOCUMENTS_TABLE")); } return this.service; diff --git a/dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/DocumentServiceImpl.java b/dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/DocumentServiceImpl.java index 8c4f5810e..9a5117f90 100644 --- a/dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/DocumentServiceImpl.java +++ b/dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/DocumentServiceImpl.java @@ -65,6 +65,7 @@ import com.formkiq.aws.dynamodb.model.DocumentTag; import com.formkiq.aws.dynamodb.model.DocumentTagType; import com.formkiq.aws.dynamodb.model.DynamicDocumentItem; +import com.formkiq.aws.dynamodb.objects.DateUtil; import com.formkiq.aws.dynamodb.objects.Objects; import com.formkiq.aws.dynamodb.objects.Strings; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; diff --git a/dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/DocumentSyncServiceDynamoDb.java b/dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/DocumentSyncServiceDynamoDb.java index 7cf4f2ca0..d2c6a81e9 100644 --- a/dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/DocumentSyncServiceDynamoDb.java +++ b/dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/DocumentSyncServiceDynamoDb.java @@ -47,6 +47,7 @@ import com.formkiq.aws.dynamodb.model.DocumentSyncServiceType; import com.formkiq.aws.dynamodb.model.DocumentSyncStatus; import com.formkiq.aws.dynamodb.model.DocumentSyncType; +import com.formkiq.aws.dynamodb.objects.DateUtil; import software.amazon.awssdk.services.dynamodb.model.AttributeValue; import software.amazon.awssdk.services.dynamodb.model.QueryResponse; diff --git a/dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/DocumentVersionServiceDynamoDb.java b/dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/DocumentVersionServiceDynamoDb.java index 8a6169482..11358464c 100644 --- a/dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/DocumentVersionServiceDynamoDb.java +++ b/dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/DocumentVersionServiceDynamoDb.java @@ -36,6 +36,7 @@ import com.formkiq.aws.dynamodb.DynamoDbConnectionBuilder; import com.formkiq.aws.dynamodb.DynamoDbService; import com.formkiq.aws.dynamodb.DynamoDbServiceImpl; +import com.formkiq.aws.dynamodb.objects.DateUtil; import com.formkiq.graalvm.annotations.Reflectable; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; import software.amazon.awssdk.services.dynamodb.model.AttributeValue; diff --git a/dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/FolderIndexProcessorImpl.java b/dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/FolderIndexProcessorImpl.java index bab75cede..9f79e375a 100644 --- a/dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/FolderIndexProcessorImpl.java +++ b/dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/FolderIndexProcessorImpl.java @@ -39,6 +39,7 @@ import com.formkiq.aws.dynamodb.DynamoDbServiceImpl; import com.formkiq.aws.dynamodb.SiteIdKeyGenerator; import com.formkiq.aws.dynamodb.model.DocumentItem; +import com.formkiq.aws.dynamodb.objects.DateUtil; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; import software.amazon.awssdk.services.dynamodb.model.AttributeValue; import software.amazon.awssdk.services.dynamodb.model.CancellationReason; diff --git a/dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/WebhooksServiceImpl.java b/dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/WebhooksServiceImpl.java index dfb58ba1a..e5403a18f 100644 --- a/dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/WebhooksServiceImpl.java +++ b/dynamodb-documents/src/main/java/com/formkiq/stacks/dynamodb/WebhooksServiceImpl.java @@ -42,6 +42,7 @@ import com.formkiq.aws.dynamodb.PaginationToAttributeValue; import com.formkiq.aws.dynamodb.QueryResponseToPagination; import com.formkiq.aws.dynamodb.model.DocumentTag; +import com.formkiq.aws.dynamodb.objects.DateUtil; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; import software.amazon.awssdk.services.dynamodb.model.AttributeValue; import software.amazon.awssdk.services.dynamodb.model.AttributeValueUpdate; diff --git a/dynamodb-documents/src/test/java/com/formkiq/stacks/dynamodb/ApiKeysServiceDynamoDbTest.java b/dynamodb-documents/src/test/java/com/formkiq/stacks/dynamodb/ApiKeysServiceDynamoDbTest.java new file mode 100644 index 000000000..795d8ba36 --- /dev/null +++ b/dynamodb-documents/src/test/java/com/formkiq/stacks/dynamodb/ApiKeysServiceDynamoDbTest.java @@ -0,0 +1,100 @@ +/** + * MIT License + * + * Copyright (c) 2018 - 2020 FormKiQ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.formkiq.stacks.dynamodb; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import com.formkiq.aws.dynamodb.DynamicObject; +import com.formkiq.testutils.aws.DynamoDbExtension; +import com.formkiq.testutils.aws.DynamoDbTestServices; + +/** + * Unit Tests for {@link ApiKeysServiceDynamoDb}. + */ +@ExtendWith(DynamoDbExtension.class) +public class ApiKeysServiceDynamoDbTest { + + /** {@link ApiKeysService}. */ + private ApiKeysService service; + + /** + * Before Test. + * + * @throws Exception Exception + */ + @BeforeEach + public void before() throws Exception { + this.service = + new ApiKeysServiceDynamoDb(DynamoDbTestServices.getDynamoDbConnection(), "Documents"); + } + + /** + * Test Create ApiKey. + * + * @throws Exception Exception + */ + @Test + public void testCreateApiKey01() throws Exception { + // given + String userId = "joe"; + for (String siteId : Arrays.asList(null, UUID.randomUUID().toString())) { + + // when + String apiKey0 = this.service.createApiKey(siteId, "test1", userId); + String apiKey1 = this.service.createApiKey(siteId, "test2", userId); + + // then + assertNotEquals(apiKey0, apiKey1); + + List list = this.service.list(); + List keys = + list.stream().map(i -> i.getString("apiKey")).collect(Collectors.toList()); + + assertEquals(2, keys.size()); + assertTrue(keys.contains(this.service.mask(apiKey0))); + assertTrue(keys.contains(this.service.mask(apiKey1))); + + assertFalse(this.service.get(apiKey0).isEmpty()); + assertFalse(this.service.get(apiKey1).isEmpty()); + assertTrue(this.service.get(UUID.randomUUID().toString()).isEmpty()); + + // when + this.service.deleteApiKey(this.service.mask(apiKey0)); + this.service.deleteApiKey(this.service.mask(apiKey1)); + + // then + assertTrue(this.service.get(apiKey0).isEmpty()); + assertTrue(this.service.get(apiKey1).isEmpty()); + } + } +} diff --git a/fkq-lambda-core/src/test/java/com/formkiq/aws/services/lambda/ConfigServiceImplTest.java b/dynamodb-documents/src/test/java/com/formkiq/stacks/dynamodb/ConfigServiceDynamoDbTest.java similarity index 90% rename from fkq-lambda-core/src/test/java/com/formkiq/aws/services/lambda/ConfigServiceImplTest.java rename to dynamodb-documents/src/test/java/com/formkiq/stacks/dynamodb/ConfigServiceDynamoDbTest.java index f35191042..206712bcf 100644 --- a/fkq-lambda-core/src/test/java/com/formkiq/aws/services/lambda/ConfigServiceImplTest.java +++ b/dynamodb-documents/src/test/java/com/formkiq/stacks/dynamodb/ConfigServiceDynamoDbTest.java @@ -21,15 +21,15 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package com.formkiq.aws.services.lambda; +package com.formkiq.stacks.dynamodb; import static com.formkiq.aws.dynamodb.SiteIdKeyGenerator.DEFAULT_SITE_ID; import static com.formkiq.aws.dynamodb.SiteIdKeyGenerator.isDefaultSiteId; -import static com.formkiq.aws.services.lambda.services.ConfigService.DOCUMENT_TIME_TO_LIVE; -import static com.formkiq.aws.services.lambda.services.ConfigService.MAX_DOCUMENTS; -import static com.formkiq.aws.services.lambda.services.ConfigService.MAX_WEBHOOKS; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; +import static com.formkiq.stacks.dynamodb.ConfigService.DOCUMENT_TIME_TO_LIVE; +import static com.formkiq.stacks.dynamodb.ConfigService.MAX_DOCUMENTS; +import static com.formkiq.stacks.dynamodb.ConfigService.MAX_WEBHOOKS; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -38,16 +38,14 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import com.formkiq.aws.dynamodb.DynamicObject; -import com.formkiq.aws.services.lambda.services.ConfigService; -import com.formkiq.aws.services.lambda.services.ConfigServiceImpl; import com.formkiq.testutils.aws.DynamoDbExtension; import com.formkiq.testutils.aws.DynamoDbTestServices; /** - * Unit Tests for {@link ConfigServiceImpl}. + * Unit Tests for {@link ConfigServiceDynamoDb}. */ @ExtendWith(DynamoDbExtension.class) -public class ConfigServiceImplTest { +public class ConfigServiceDynamoDbTest { /** {@link ConfigService}. */ private ConfigService service; @@ -59,7 +57,8 @@ public class ConfigServiceImplTest { */ @BeforeEach public void before() throws Exception { - this.service = new ConfigServiceImpl(DynamoDbTestServices.getDynamoDbConnection(), "Documents"); + this.service = + new ConfigServiceDynamoDb(DynamoDbTestServices.getDynamoDbConnection(), "Documents"); } /** diff --git a/dynamodb-documents/src/test/java/com/formkiq/stacks/dynamodb/DateUtilTest.java b/dynamodb-documents/src/test/java/com/formkiq/stacks/dynamodb/DateUtilTest.java index 212625225..69cb376f5 100644 --- a/dynamodb-documents/src/test/java/com/formkiq/stacks/dynamodb/DateUtilTest.java +++ b/dynamodb-documents/src/test/java/com/formkiq/stacks/dynamodb/DateUtilTest.java @@ -23,13 +23,14 @@ */ package com.formkiq.stacks.dynamodb; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; import java.util.TimeZone; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import com.formkiq.aws.dynamodb.objects.DateUtil; /** * Unit Tests for {@link DateUtil}. @@ -43,7 +44,7 @@ public class DateUtilTest { /** * before. */ - @Before + @BeforeEach public void before() { this.df.setTimeZone(TimeZone.getTimeZone("UTC")); } diff --git a/dynamodb-documents/src/test/java/com/formkiq/stacks/dynamodb/DocumentCountServiceDynamoDbTest.java b/dynamodb-documents/src/test/java/com/formkiq/stacks/dynamodb/DocumentCountServiceDynamoDbTest.java index a6dfd0c8e..38c5b387c 100644 --- a/dynamodb-documents/src/test/java/com/formkiq/stacks/dynamodb/DocumentCountServiceDynamoDbTest.java +++ b/dynamodb-documents/src/test/java/com/formkiq/stacks/dynamodb/DocumentCountServiceDynamoDbTest.java @@ -24,8 +24,8 @@ package com.formkiq.stacks.dynamodb; import static com.formkiq.testutils.aws.DynamoDbExtension.DOCUMENTS_TABLE; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.UUID; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/dynamodb-documents/src/test/java/com/formkiq/stacks/dynamodb/DocumentSearchServiceImplTest.java b/dynamodb-documents/src/test/java/com/formkiq/stacks/dynamodb/DocumentSearchServiceImplTest.java index 2eedd50da..2605f57e9 100644 --- a/dynamodb-documents/src/test/java/com/formkiq/stacks/dynamodb/DocumentSearchServiceImplTest.java +++ b/dynamodb-documents/src/test/java/com/formkiq/stacks/dynamodb/DocumentSearchServiceImplTest.java @@ -25,10 +25,10 @@ import static com.formkiq.stacks.dynamodb.DocumentService.MAX_RESULTS; import static com.formkiq.testutils.aws.DynamoDbExtension.DOCUMENTS_TABLE; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import java.nio.charset.StandardCharsets; import java.text.SimpleDateFormat; import java.time.ZonedDateTime; @@ -54,6 +54,7 @@ import com.formkiq.aws.dynamodb.model.SearchMetaCriteria; import com.formkiq.aws.dynamodb.model.SearchQuery; import com.formkiq.aws.dynamodb.model.SearchTagCriteria; +import com.formkiq.aws.dynamodb.objects.DateUtil; import com.formkiq.testutils.aws.DynamoDbExtension; import com.formkiq.testutils.aws.DynamoDbTestServices; diff --git a/dynamodb-documents/src/test/java/com/formkiq/stacks/dynamodb/DocumentServiceImplTest.java b/dynamodb-documents/src/test/java/com/formkiq/stacks/dynamodb/DocumentServiceImplTest.java index 4a99709d8..40fb7346d 100644 --- a/dynamodb-documents/src/test/java/com/formkiq/stacks/dynamodb/DocumentServiceImplTest.java +++ b/dynamodb-documents/src/test/java/com/formkiq/stacks/dynamodb/DocumentServiceImplTest.java @@ -75,6 +75,7 @@ import com.formkiq.aws.dynamodb.model.SearchMetaCriteria; import com.formkiq.aws.dynamodb.model.SearchQuery; import com.formkiq.aws.dynamodb.model.SearchTagCriteria; +import com.formkiq.aws.dynamodb.objects.DateUtil; import com.formkiq.testutils.aws.DynamoDbExtension; import com.formkiq.testutils.aws.DynamoDbTestServices; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; diff --git a/dynamodb-documents/src/test/java/com/formkiq/stacks/dynamodb/DocumentSyncServiceDynamoDbTest.java b/dynamodb-documents/src/test/java/com/formkiq/stacks/dynamodb/DocumentSyncServiceDynamoDbTest.java index c50681e45..48927c897 100644 --- a/dynamodb-documents/src/test/java/com/formkiq/stacks/dynamodb/DocumentSyncServiceDynamoDbTest.java +++ b/dynamodb-documents/src/test/java/com/formkiq/stacks/dynamodb/DocumentSyncServiceDynamoDbTest.java @@ -26,8 +26,8 @@ import static com.formkiq.aws.dynamodb.model.DocumentSyncServiceType.TYPESENSE; import static com.formkiq.stacks.dynamodb.DocumentSyncService.MESSAGE_ADDED_METADATA; import static com.formkiq.testutils.aws.DynamoDbExtension.DOCUMENT_SYNCS_TABLE; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import java.net.URISyntaxException; import java.util.Arrays; import java.util.UUID; diff --git a/dynamodb-documents/src/test/java/com/formkiq/stacks/dynamodb/SiteIdKeyGeneratorTest.java b/dynamodb-documents/src/test/java/com/formkiq/stacks/dynamodb/SiteIdKeyGeneratorTest.java index de3ab374b..0727ec7f1 100644 --- a/dynamodb-documents/src/test/java/com/formkiq/stacks/dynamodb/SiteIdKeyGeneratorTest.java +++ b/dynamodb-documents/src/test/java/com/formkiq/stacks/dynamodb/SiteIdKeyGeneratorTest.java @@ -29,10 +29,10 @@ import static com.formkiq.aws.dynamodb.SiteIdKeyGenerator.getDocumentId; import static com.formkiq.aws.dynamodb.SiteIdKeyGenerator.getSiteId; import static com.formkiq.aws.dynamodb.SiteIdKeyGenerator.resetDatabaseKey; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import java.util.UUID; -import org.junit.Test; +import org.junit.jupiter.api.Test; import com.formkiq.aws.dynamodb.SiteIdKeyGenerator; /** diff --git a/dynamodb-documents/src/test/java/com/formkiq/stacks/dynamodb/WebhooksServiceImplTest.java b/dynamodb-documents/src/test/java/com/formkiq/stacks/dynamodb/WebhooksServiceImplTest.java index e7e1c6984..a41630846 100644 --- a/dynamodb-documents/src/test/java/com/formkiq/stacks/dynamodb/WebhooksServiceImplTest.java +++ b/dynamodb-documents/src/test/java/com/formkiq/stacks/dynamodb/WebhooksServiceImplTest.java @@ -23,9 +23,9 @@ */ package com.formkiq.stacks.dynamodb; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.time.ZonedDateTime; diff --git a/fkq-lambda-core/build.gradle b/fkq-lambda-core/build.gradle index 60d924777..40b77e064 100644 --- a/fkq-lambda-core/build.gradle +++ b/fkq-lambda-core/build.gradle @@ -12,9 +12,9 @@ dependencies { implementation project(':fkq-validation') implementation group: 'com.amazonaws', name: 'aws-lambda-java-core', version: '1.2.2' - implementation group: 'com.google.code.gson', name: 'gson', version: '2.10' - implementation group: 'com.formkiq', name: 'graalvm-annotations', version: '1.1.0' - implementation group: 'software.amazon.awssdk', name: 'utils', version: '2.19.2' + implementation group: 'com.google.code.gson', name: 'gson', version: '2.10.1' + implementation group: 'com.formkiq', name: 'graalvm-annotations', version: '1.2.0' + implementation group: 'software.amazon.awssdk', name: 'utils', version: '2.20.33' testImplementation project(':fkq-test-utils') testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version:'5.9.1' diff --git a/fkq-lambda-core/src/main/java/com/formkiq/aws/services/lambda/AbstractRestApiRequestHandler.java b/fkq-lambda-core/src/main/java/com/formkiq/aws/services/lambda/AbstractRestApiRequestHandler.java index 547d1b079..56521dcde 100644 --- a/fkq-lambda-core/src/main/java/com/formkiq/aws/services/lambda/AbstractRestApiRequestHandler.java +++ b/fkq-lambda-core/src/main/java/com/formkiq/aws/services/lambda/AbstractRestApiRequestHandler.java @@ -43,6 +43,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.stream.Collectors; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.LambdaLogger; import com.amazonaws.services.lambda.runtime.RequestStreamHandler; @@ -229,6 +230,30 @@ private ApiGatewayRequestEvent getApiGatewayEvent(final String str, final Lambda } ApiGatewayRequestEvent event = this.gson.fromJson(str, ApiGatewayRequestEvent.class); + + if (event != null) { + ApiGatewayRequestContext requestContext = + event.getRequestContext() != null ? event.getRequestContext() + : new ApiGatewayRequestContext(); + + Map identity = + requestContext.getIdentity() != null ? requestContext.getIdentity() : Map.of(); + + String s = String.format( + "{\"requestId\": \"%s\",\"ip\": \"%s\",\"requestTime\": \"%s\",\"httpMethod\": \"%s\"," + + "\"routeKey\": \"%s\",\"pathParameters\": %s," + + "\"protocol\": \"%s\",\"user\":\"%s\",\"queryParameters\":%s}", + requestContext.getRequestId(), identity.get("sourceIp"), requestContext.getRequestTime(), + event.getHttpMethod(), event.getHttpMethod() + " " + event.getResource(), + "{" + toStringFromMap(event.getPathParameters()) + "}", requestContext.getProtocol(), + ApiGatewayRequestEventUtil.getCallingCognitoUsername(event), + "{" + toStringFromMap(event.getQueryStringParameters()) + "}"); + + logger.log(s); + } else { + logger.log("invalid request event"); + } + return event; } @@ -457,6 +482,13 @@ private void processResponse(final ApiAuthorizer authorizer, final ApiGatewayReq } } + private String toStringFromMap(final Map map) { + return map != null + ? map.entrySet().stream().map(e -> String.format("\"%s\":\"%s\"", e.getKey(), e.getValue())) + .collect(Collectors.joining(",")) + : ""; + } + /** * Write JSON Response {@link OutputStream}. * diff --git a/fkq-lambda-core/src/main/java/com/formkiq/aws/services/lambda/ApiAuthorizer.java b/fkq-lambda-core/src/main/java/com/formkiq/aws/services/lambda/ApiAuthorizer.java index 5e44b10ae..e20336bfd 100644 --- a/fkq-lambda-core/src/main/java/com/formkiq/aws/services/lambda/ApiAuthorizer.java +++ b/fkq-lambda-core/src/main/java/com/formkiq/aws/services/lambda/ApiAuthorizer.java @@ -105,7 +105,6 @@ public String accessSummary() { * @param userAuthentication {@link ApiAuthorizerType} * @return {@link List} {@link String} */ - @SuppressWarnings("unchecked") private List getCognitoGroups(final ApiAuthorizerType userAuthentication) { List groups = Collections.emptyList(); @@ -117,16 +116,14 @@ private List getCognitoGroups(final ApiAuthorizerType userAuthentication Map authorizer = requestContext.getAuthorizer(); - if (authorizer != null && authorizer.containsKey("claims")) { + Map claims = getAuthorizerClaims(authorizer); - Map claims = (Map) authorizer.get("claims"); - if (claims.containsKey("cognito:groups")) { - Object obj = claims.get("cognito:groups"); - if (obj != null) { - String s = obj.toString().replaceFirst("^\\[", "").replaceAll("\\]$", ""); - groups = new ArrayList<>(Arrays.asList(s.split(" "))); - groups.removeIf(g -> g.length() == 0); - } + if (claims.containsKey("cognito:groups")) { + Object obj = claims.get("cognito:groups"); + if (obj != null) { + String s = obj.toString().replaceFirst("^\\[", "").replaceAll("\\]$", ""); + groups = new ArrayList<>(Arrays.asList(s.split(" "))); + groups.removeIf(g -> g.length() == 0); } } } @@ -142,6 +139,21 @@ private List getCognitoGroups(final ApiAuthorizerType userAuthentication return groups; } + @SuppressWarnings("unchecked") + static Map getAuthorizerClaims(final Map authorizer) { + Map claims = Collections.emptyMap(); + + if (authorizer != null && authorizer.containsKey("claims")) { + claims = (Map) authorizer.get("claims"); + } + + if (claims == null && authorizer != null && authorizer.containsKey("apiKeyClaims")) { + claims = (Map) authorizer.get("apiKeyClaims"); + } + + return claims; + } + /** * Get the List of Possible SiteIds for User. * diff --git a/fkq-lambda-core/src/main/java/com/formkiq/aws/services/lambda/ApiGatewayRequestContext.java b/fkq-lambda-core/src/main/java/com/formkiq/aws/services/lambda/ApiGatewayRequestContext.java index 3edad4110..abae3e874 100644 --- a/fkq-lambda-core/src/main/java/com/formkiq/aws/services/lambda/ApiGatewayRequestContext.java +++ b/fkq-lambda-core/src/main/java/com/formkiq/aws/services/lambda/ApiGatewayRequestContext.java @@ -39,6 +39,12 @@ public class ApiGatewayRequestContext { private String domainName; /** {@link Map} of Identity. */ private Map identity; + /** Protocol. */ + private String protocol; + /** Request Id. */ + private String requestId; + /** Request Time. */ + private String requestTime; /** * constructor. @@ -72,6 +78,33 @@ public Map getIdentity() { return this.identity; } + /** + * Get Protocol. + * + * @return {@link String} + */ + public String getProtocol() { + return this.protocol; + } + + /** + * Get Request Id. + * + * @return {@link String} + */ + public String getRequestId() { + return this.requestId; + } + + /** + * Get Request Time. + * + * @return {@link String} + */ + public String getRequestTime() { + return this.requestTime; + } + /** * Set Authorizer. * @@ -98,4 +131,31 @@ public void setDomainName(final String domain) { public void setIdentity(final Map map) { this.identity = map; } + + /** + * Set Procotol. + * + * @param requestProtocol {@link String} + */ + public void setProtocol(final String requestProtocol) { + this.protocol = requestProtocol; + } + + /** + * Set Request Id. + * + * @param id {@link String} + */ + public void setRequestId(final String id) { + this.requestId = id; + } + + /** + * Set Request Time. + * + * @param time {@link String} + */ + public void setRequestTime(final String time) { + this.requestTime = time; + } } diff --git a/fkq-lambda-core/src/main/java/com/formkiq/aws/services/lambda/ApiGatewayRequestEventUtil.java b/fkq-lambda-core/src/main/java/com/formkiq/aws/services/lambda/ApiGatewayRequestEventUtil.java index c5ddefea0..6eadb1c05 100644 --- a/fkq-lambda-core/src/main/java/com/formkiq/aws/services/lambda/ApiGatewayRequestEventUtil.java +++ b/fkq-lambda-core/src/main/java/com/formkiq/aws/services/lambda/ApiGatewayRequestEventUtil.java @@ -192,8 +192,7 @@ static String getBodyAsString(final ApiGatewayRequestEvent event) throws BadExce * @param event {@link ApiGatewayRequestEvent}. * @return {@link String} */ - @SuppressWarnings("unchecked") - default String getCallingCognitoUsername(final ApiGatewayRequestEvent event) { + static String getCallingCognitoUsername(final ApiGatewayRequestEvent event) { String username = null; @@ -217,20 +216,18 @@ default String getCallingCognitoUsername(final ApiGatewayRequestEvent event) { } } - if (authorizer != null && authorizer.containsKey("claims")) { + Map claims = ApiAuthorizer.getAuthorizerClaims(authorizer); - Map claims = (Map) authorizer.get("claims"); - String u = getCallingCognitoUsernameFromClaims(claims); - if (u != null) { - username = u; - } + String u = getCallingCognitoUsernameFromClaims(claims); + if (u != null) { + username = u; } } return username; } - private String getCallingCognitoUsernameFromClaims(Map claims) { + private static String getCallingCognitoUsernameFromClaims(final Map claims) { String username = null; if (claims.containsKey("email")) { username = claims.get("email").toString(); diff --git a/fkq-lambda-core/src/main/java/com/formkiq/aws/services/lambda/ApiGatewayRequestHandler.java b/fkq-lambda-core/src/main/java/com/formkiq/aws/services/lambda/ApiGatewayRequestHandler.java index 5620e72f1..be44128e5 100644 --- a/fkq-lambda-core/src/main/java/com/formkiq/aws/services/lambda/ApiGatewayRequestHandler.java +++ b/fkq-lambda-core/src/main/java/com/formkiq/aws/services/lambda/ApiGatewayRequestHandler.java @@ -81,9 +81,10 @@ default boolean hasAccess(String method, String path, ApiAuthorizer authorizer) * @param event {@link ApiGatewayRequestEvent} * @param authorizer {@link ApiAuthorizer} * @param awsServices {@link AwsServiceCache} + * @throws Exception Exception */ default void beforeDelete(LambdaLogger logger, ApiGatewayRequestEvent event, - ApiAuthorizer authorizer, AwsServiceCache awsServices) { + ApiAuthorizer authorizer, AwsServiceCache awsServices) throws Exception { // empty } @@ -95,9 +96,10 @@ default void beforeDelete(LambdaLogger logger, ApiGatewayRequestEvent event, * @param event {@link ApiGatewayRequestEvent} * @param authorizer {@link ApiAuthorizer} * @param awsServices {@link AwsServiceCache} + * @throws Exception Exception */ default void beforeGet(LambdaLogger logger, ApiGatewayRequestEvent event, - ApiAuthorizer authorizer, AwsServiceCache awsServices) { + ApiAuthorizer authorizer, AwsServiceCache awsServices) throws Exception { // empty } @@ -108,9 +110,10 @@ default void beforeGet(LambdaLogger logger, ApiGatewayRequestEvent event, * @param event {@link ApiGatewayRequestEvent} * @param authorizer {@link ApiAuthorizer} * @param awsServices {@link AwsServiceCache} + * @throws Exception Exception */ default void beforeHead(LambdaLogger logger, ApiGatewayRequestEvent event, - ApiAuthorizer authorizer, AwsServiceCache awsServices) { + ApiAuthorizer authorizer, AwsServiceCache awsServices) throws Exception { // empty } @@ -121,9 +124,10 @@ default void beforeHead(LambdaLogger logger, ApiGatewayRequestEvent event, * @param event {@link ApiGatewayRequestEvent} * @param authorizer {@link ApiAuthorizer} * @param awsServices {@link AwsServiceCache} + * @throws Exception Exception */ default void beforePatch(LambdaLogger logger, ApiGatewayRequestEvent event, - ApiAuthorizer authorizer, AwsServiceCache awsServices) { + ApiAuthorizer authorizer, AwsServiceCache awsServices) throws Exception { // empty } @@ -134,9 +138,10 @@ default void beforePatch(LambdaLogger logger, ApiGatewayRequestEvent event, * @param event {@link ApiGatewayRequestEvent} * @param authorizer {@link ApiAuthorizer} * @param awsServices {@link AwsServiceCache} + * @throws Exception Exception */ default void beforePost(LambdaLogger logger, ApiGatewayRequestEvent event, - ApiAuthorizer authorizer, AwsServiceCache awsServices) { + ApiAuthorizer authorizer, AwsServiceCache awsServices) throws Exception { // empty } @@ -147,9 +152,10 @@ default void beforePost(LambdaLogger logger, ApiGatewayRequestEvent event, * @param event {@link ApiGatewayRequestEvent} * @param authorizer {@link ApiAuthorizer} * @param awsServices {@link AwsServiceCache} + * @throws Exception Exception */ default void beforePut(LambdaLogger logger, ApiGatewayRequestEvent event, - ApiAuthorizer authorizer, AwsServiceCache awsServices) { + ApiAuthorizer authorizer, AwsServiceCache awsServices) throws Exception { // empty } diff --git a/fkq-lambda-core/src/test/java/com/formkiq/aws/services/lambda/DynamoDbCacheServiceTest.java b/fkq-lambda-core/src/test/java/com/formkiq/aws/services/lambda/DynamoDbCacheServiceTest.java index d95810e59..47699e3c9 100644 --- a/fkq-lambda-core/src/test/java/com/formkiq/aws/services/lambda/DynamoDbCacheServiceTest.java +++ b/fkq-lambda-core/src/test/java/com/formkiq/aws/services/lambda/DynamoDbCacheServiceTest.java @@ -23,8 +23,8 @@ */ package com.formkiq.aws.services.lambda; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.util.Date; diff --git a/fkq-lambda-services/build.gradle b/fkq-lambda-services/build.gradle index 7a6110514..1a57ba2dd 100644 --- a/fkq-lambda-services/build.gradle +++ b/fkq-lambda-services/build.gradle @@ -6,6 +6,5 @@ dependencies { test { failFast = true - useJUnitPlatform() - exclude 'com/formkiq/module/lambdaservices/awstest/**' + useJUnitPlatform() } diff --git a/fkq-test-utils/build.gradle b/fkq-test-utils/build.gradle index 499a0181f..04554c3f2 100644 --- a/fkq-test-utils/build.gradle +++ b/fkq-test-utils/build.gradle @@ -8,10 +8,10 @@ dependencies { implementation project(':aws-sqs') implementation project(':aws-ssm') implementation project(':aws-dynamodb') - implementation group: 'com.amazonaws', name: 'aws-java-sdk', version: '1.12.370' + implementation group: 'com.amazonaws', name: 'aws-java-sdk', version: '1.12.436' implementation group: 'com.amazonaws', name: 'aws-lambda-java-core', version: '1.2.2' - implementation group: 'com.formkiq.stacks', name: 'client', version:'1.9.0' - implementation group: 'com.google.code.gson', name: 'gson', version: '2.10' + implementation group: 'com.formkiq.stacks', name: 'client', version:'1.10.0' + implementation group: 'com.google.code.gson', name: 'gson', version: '2.10.1' implementation group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version:'5.9.1' implementation group: 'org.testcontainers', name: 'testcontainers', version: '1.17.6' implementation group: 'org.testcontainers', name: 'localstack', version: '1.17.6' diff --git a/fkq-test-utils/src/main/java/com/formkiq/testutils/aws/DynamoDbTestServices.java b/fkq-test-utils/src/main/java/com/formkiq/testutils/aws/DynamoDbTestServices.java index c9cfb0e93..8d42b5fd8 100644 --- a/fkq-test-utils/src/main/java/com/formkiq/testutils/aws/DynamoDbTestServices.java +++ b/fkq-test-utils/src/main/java/com/formkiq/testutils/aws/DynamoDbTestServices.java @@ -63,7 +63,7 @@ public static DynamoDbConnectionBuilder getDynamoDbConnection() throws URISyntax AwsCredentialsProvider cred = StaticCredentialsProvider.create(AwsBasicCredentials.create("ACCESSKEY", "SECRETKEY")); - dbConnection = new DynamoDbConnectionBuilder().setRegion(AWS_REGION).setCredentials(cred) + dbConnection = new DynamoDbConnectionBuilder(false).setRegion(AWS_REGION).setCredentials(cred) .setEndpointOverride(getEndpoint()); } diff --git a/fkq-test-utils/src/main/java/com/formkiq/testutils/aws/FkqCognitoService.java b/fkq-test-utils/src/main/java/com/formkiq/testutils/aws/FkqCognitoService.java index 63891a630..c2f29e570 100644 --- a/fkq-test-utils/src/main/java/com/formkiq/testutils/aws/FkqCognitoService.java +++ b/fkq-test-utils/src/main/java/com/formkiq/testutils/aws/FkqCognitoService.java @@ -51,6 +51,8 @@ public class FkqCognitoService { private String rootHttpUrl; /** FormKiQ IAM Api Url. */ private String rootRestUrl; + /** FormKiQ Key Api Url. */ + private String rootKeyUrl; /** * constructor. @@ -72,6 +74,9 @@ public FkqCognitoService(final String awsProfile, final Region awsRegion, this.rootRestUrl = this.ssm.getParameterValue("/formkiq/" + appEnvironment + "/api/DocumentsIamUrl"); + this.rootKeyUrl = + this.ssm.getParameterValue("/formkiq/" + appEnvironment + "/api/DocumentsKeyUrl"); + String cognitoUserPoolId = this.ssm.getParameterValue("/formkiq/" + appEnvironment + "/cognito/UserPoolId"); @@ -127,6 +132,19 @@ public FormKiqClientV1 getFormKiqClient(final AuthenticationResultType token) { return new FormKiqClientV1(connection); } + /** + * Get FormKiQ Key API Url. + * + * @param apiKey {@link String} + * @return {@link FormKiqClientV1} + */ + public FormKiqClientV1 getFormKiqClient(final String apiKey) { + FormKiqClientConnection connection = new FormKiqClientConnection(this.rootKeyUrl) + .cognitoIdToken(apiKey).header("Origin", Arrays.asList("http://localhost")) + .header("Access-Control-Request-Method", Arrays.asList("GET")); + return new FormKiqClientV1(connection); + } + /** * Get FormKiQ IAM Url. * diff --git a/fkq-test-utils/src/main/java/com/formkiq/testutils/aws/FkqDocumentService.java b/fkq-test-utils/src/main/java/com/formkiq/testutils/aws/FkqDocumentService.java index 9a46c8137..7454d02e3 100644 --- a/fkq-test-utils/src/main/java/com/formkiq/testutils/aws/FkqDocumentService.java +++ b/fkq-test-utils/src/main/java/com/formkiq/testutils/aws/FkqDocumentService.java @@ -31,6 +31,7 @@ import java.net.http.HttpRequest.BodyPublishers; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; +import java.util.Base64; import java.util.List; import java.util.Map; import java.util.Optional; @@ -124,7 +125,33 @@ public static String addDocumentWithActions(final FormKiqClient client, final St return client.addDocument(new AddDocumentRequest().siteId(siteId).document(new AddDocument() .path(path).content(content).contentType(contentType).tags(tags).actions(actions))) .documentId(); + } + /** + * Add Document with Actions. + * + * @param client {@link FormKiqClient} + * @param siteId {@link String} + * @param path {@link String} + * @param content {@link String} + * @param contentType {@link String} + * @param actions {@link List} {@link AddDocumentAction} + * @param tags {@link List} {@link DocumentTag} + * @return {@link String} + * @throws IOException IOException + * @throws InterruptedException InterruptedException + */ + public static String addDocumentWithActions(final FormKiqClient client, final String siteId, + final String path, final byte[] content, final String contentType, + final List actions, final List tags) + throws IOException, InterruptedException { + + String base64 = Base64.getEncoder().encodeToString(content); + + return client + .addDocument(new AddDocumentRequest().siteId(siteId).document(new AddDocument().path(path) + .contentAsBase64(base64).contentType(contentType).tags(tags).actions(actions))) + .documentId(); } /** diff --git a/fkq-test-utils/src/main/java/com/formkiq/testutils/aws/FkqS3Service.java b/fkq-test-utils/src/main/java/com/formkiq/testutils/aws/FkqS3Service.java index fd7216e4f..f7d99148d 100644 --- a/fkq-test-utils/src/main/java/com/formkiq/testutils/aws/FkqS3Service.java +++ b/fkq-test-utils/src/main/java/com/formkiq/testutils/aws/FkqS3Service.java @@ -41,6 +41,6 @@ public class FkqS3Service extends S3Service { * @param awsRegion {@link Region} */ public FkqS3Service(final String awsProfile, final Region awsRegion) { - super(new S3ConnectionBuilder().setCredentials(awsProfile).setRegion(awsRegion)); + super(new S3ConnectionBuilder(false).setCredentials(awsProfile).setRegion(awsRegion)); } } diff --git a/fkq-test-utils/src/main/java/com/formkiq/testutils/aws/FkqSsmService.java b/fkq-test-utils/src/main/java/com/formkiq/testutils/aws/FkqSsmService.java index bdc900006..6d1ad1f2e 100644 --- a/fkq-test-utils/src/main/java/com/formkiq/testutils/aws/FkqSsmService.java +++ b/fkq-test-utils/src/main/java/com/formkiq/testutils/aws/FkqSsmService.java @@ -48,7 +48,8 @@ public class FkqSsmService implements SsmService { * @param awsRegion {@link Region} */ public FkqSsmService(final String awsProfile, final Region awsRegion) { - this.ssmBuilder = new SsmConnectionBuilder().setCredentials(awsProfile).setRegion(awsRegion); + this.ssmBuilder = + new SsmConnectionBuilder(false).setCredentials(awsProfile).setRegion(awsRegion); this.service = new SsmServiceImpl(this.ssmBuilder); } diff --git a/fkq-test-utils/src/main/java/com/formkiq/testutils/aws/LocalStackExtension.java b/fkq-test-utils/src/main/java/com/formkiq/testutils/aws/LocalStackExtension.java index 5cb2e2169..c7e11b668 100644 --- a/fkq-test-utils/src/main/java/com/formkiq/testutils/aws/LocalStackExtension.java +++ b/fkq-test-utils/src/main/java/com/formkiq/testutils/aws/LocalStackExtension.java @@ -25,6 +25,7 @@ import static com.formkiq.testutils.aws.TestServices.BUCKET_NAME; import static com.formkiq.testutils.aws.TestServices.FORMKIQ_APP_ENVIRONMENT; +import static com.formkiq.testutils.aws.TestServices.OCR_BUCKET_NAME; import static com.formkiq.testutils.aws.TestServices.STAGE_BUCKET_NAME; import org.junit.jupiter.api.extension.BeforeAllCallback; import org.junit.jupiter.api.extension.ExtensionContext; @@ -54,6 +55,10 @@ public void beforeAll(final ExtensionContext context) throws Exception { s3service.createBucket(STAGE_BUCKET_NAME); } + if (!s3service.exists(OCR_BUCKET_NAME)) { + s3service.createBucket(OCR_BUCKET_NAME); + } + new SsmServiceImpl(TestServices.getSsmConnection(null)) .putParameter("/formkiq/" + FORMKIQ_APP_ENVIRONMENT + "/version", "1.1"); } diff --git a/fkq-test-utils/src/main/java/com/formkiq/testutils/aws/TestServices.java b/fkq-test-utils/src/main/java/com/formkiq/testutils/aws/TestServices.java index 873ec35dc..279a97cc4 100644 --- a/fkq-test-utils/src/main/java/com/formkiq/testutils/aws/TestServices.java +++ b/fkq-test-utils/src/main/java/com/formkiq/testutils/aws/TestServices.java @@ -83,6 +83,8 @@ public final class TestServices { private static SsmConnectionBuilder ssmConnection; /** {@link String}. */ public static final String STAGE_BUCKET_NAME = "stagebucket"; + /** {@link String}. */ + public static final String OCR_BUCKET_NAME = "ocrbucket"; /** * Get Local Stack Endpoint. @@ -146,7 +148,7 @@ public static synchronized S3ConnectionBuilder getS3Connection(final URI endpoin AwsCredentialsProvider cred = StaticCredentialsProvider .create(AwsSessionCredentials.create("ACCESSKEY", "SECRETKEY", "TOKENKEY")); - s3Connection = new S3ConnectionBuilder().setCredentials(cred).setRegion(AWS_REGION) + s3Connection = new S3ConnectionBuilder(false).setCredentials(cred).setRegion(AWS_REGION) .setEndpointOverride(getEndpoint(Service.S3, endpointOverride)); } @@ -167,7 +169,7 @@ public static synchronized SnsConnectionBuilder getSnsConnection(final URI endpo AwsCredentialsProvider cred = StaticCredentialsProvider .create(AwsSessionCredentials.create("ACCESSKEY", "SECRETKEY", "TOKENKEY")); - snsConnection = new SnsConnectionBuilder().setCredentials(cred).setRegion(AWS_REGION) + snsConnection = new SnsConnectionBuilder(false).setCredentials(cred).setRegion(AWS_REGION) .setEndpointOverride(getEndpoint(Service.SNS, endpointOverride)); } @@ -188,7 +190,7 @@ public static synchronized SqsConnectionBuilder getSqsConnection(final URI endpo AwsCredentialsProvider cred = StaticCredentialsProvider .create(AwsSessionCredentials.create("ACCESSKEY", "SECRETKEY", "TOKENKEY")); - sqsConnection = new SqsConnectionBuilder().setCredentials(cred).setRegion(AWS_REGION) + sqsConnection = new SqsConnectionBuilder(false).setCredentials(cred).setRegion(AWS_REGION) .setEndpointOverride(getEndpoint(Service.SQS, endpointOverride)); } @@ -260,7 +262,7 @@ public static synchronized SsmConnectionBuilder getSsmConnection(final URI endpo AwsCredentialsProvider cred = StaticCredentialsProvider .create(AwsSessionCredentials.create("ACCESSKEY", "SECRETKEY", "TOKENKEY")); - ssmConnection = new SsmConnectionBuilder().setCredentials(cred).setRegion(AWS_REGION) + ssmConnection = new SsmConnectionBuilder(false).setCredentials(cred).setRegion(AWS_REGION) .setEndpointOverride(getEndpoint(Service.SSM, endpointOverride)); } diff --git a/fkq-validation/build.gradle b/fkq-validation/build.gradle index 5a5b332eb..10ed35277 100644 --- a/fkq-validation/build.gradle +++ b/fkq-validation/build.gradle @@ -2,5 +2,5 @@ description = "FKQ Validation Library" dependencies { - implementation group: 'com.formkiq', name: 'graalvm-annotations', version: '1.1.0' + implementation group: 'com.formkiq', name: 'graalvm-annotations', version: '1.2.0' } diff --git a/gradle.properties b/gradle.properties index 9a23a1ee0..6f2ea7514 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,4 +14,4 @@ org.gradle.jvmargs=-Xmx4096m testregion=us-east-1 testappenvironment=test testprofile=default -testadminemail= +testchatgptapikey= diff --git a/http-sigv4/build.gradle b/http-sigv4/build.gradle index b8545dd64..7df01b58a 100644 --- a/http-sigv4/build.gradle +++ b/http-sigv4/build.gradle @@ -3,6 +3,6 @@ description = "Http Sigv4" dependencies { api project(':http') - implementation group: 'software.amazon.awssdk', name: 'http-client-spi', version: '2.19.2' - api group: 'software.amazon.awssdk', name: 'auth', version: '2.19.2' + implementation group: 'software.amazon.awssdk', name: 'http-client-spi', version: '2.20.33' + api group: 'software.amazon.awssdk', name: 'auth', version: '2.20.33' } \ No newline at end of file diff --git a/http/build.gradle b/http/build.gradle index 32da6b136..6aacea079 100644 --- a/http/build.gradle +++ b/http/build.gradle @@ -2,5 +2,5 @@ description = "Http" dependencies { - implementation group: 'com.google.code.gson', name: 'gson', version: '2.10' + implementation group: 'com.google.code.gson', name: 'gson', version: '2.10.1' } \ No newline at end of file diff --git a/lambda-api/.classpath b/lambda-api/.classpath index 66f8050c0..58920f4a9 100644 --- a/lambda-api/.classpath +++ b/lambda-api/.classpath @@ -3,29 +3,41 @@ - + - + - + - + + + + + + + + + + + + + diff --git a/lambda-api/Dockerfile b/lambda-api/Dockerfile deleted file mode 100644 index 7fee6d5e6..000000000 --- a/lambda-api/Dockerfile +++ /dev/null @@ -1,7 +0,0 @@ -FROM public.ecr.aws/lambda/provided - -COPY ./runtime/bootstrap /var/runtime/ -COPY ./build/graalvm/server /var/task/ - -# Set the CMD to your handler (could also be done as a parameter override outside of the Dockerfile) -CMD [ "com.formkiq.stacks.api.CoreRequestHandler" ] diff --git a/lambda-api/build.gradle b/lambda-api/build.gradle index 7fec669ad..c0ce81c62 100644 --- a/lambda-api/build.gradle +++ b/lambda-api/build.gradle @@ -1,9 +1,33 @@ description = "Lambda API to Dynamodb" +sourceSets { + integration { + java.srcDir "$projectDir/src/integration/java" + resources.srcDir "$projectDir/src/integration/resources" + compileClasspath += main.output + test.output + runtimeClasspath += main.output + test.output + } +} + +configurations { + integrationImplementation.extendsFrom testImplementation + integrationRuntime.extendsFrom testRuntime +} + +def getCmd() { + String os = System.getProperty("os.name").toLowerCase() + return os.contains("win") ? "cmd" : "bash" +} + +def getCmdParam() { + String os = System.getProperty("os.name").toLowerCase() + return os.contains("win") ? "/c" : "-c" +} + dependencies { - annotationProcessor group: 'com.formkiq', name: 'graalvm-annotations-processor', version: '1.2.0' + annotationProcessor group: 'com.formkiq', name: 'graalvm-annotations-processor', version: '1.3.0' implementation project(':fkq-lambda-services') implementation project(':fkq-lambda-core') @@ -17,33 +41,33 @@ dependencies { implementation project(':dynamodb-documents') implementation project(':actions') implementation project(':typesense') + implementation project(':ocr') implementation project(':http-sigv4') implementation group: 'com.amazonaws', name: 'aws-lambda-java-core', version: '1.2.2' - implementation group: 'com.google.code.gson', name: 'gson', version: '2.10' + implementation group: 'com.google.code.gson', name: 'gson', version: '2.10.1' implementation group: 'com.formkiq', name: 'lambda-runtime-graalvm', version:'2.3.1' - implementation group: 'com.formkiq', name: 'graalvm-annotations', version: '1.1.0' + implementation group: 'com.formkiq', name: 'graalvm-annotations', version: '1.2.0' - implementation group: 'org.slf4j', name: 'slf4j-simple', version: '2.0.6' + implementation group: 'org.slf4j', name: 'slf4j-simple', version: '2.0.7' - testImplementation group: 'com.formkiq.stacks', name: 'client', version:'1.10.0-SNAPSHOT' + testImplementation group: 'com.formkiq.stacks', name: 'client', version:'1.11.0' testImplementation project(':aws-cognito') testImplementation project(':aws-iam') testImplementation project(':aws-sts') testImplementation project(':fkq-test-utils') testImplementation project(':lambda-typesense') - testImplementation group: 'software.amazon.awssdk', name: 'apigateway', version: '2.19.2' - testImplementation group: 'software.amazon.awssdk', name: 'sts', version: '2.19.2' + testImplementation group: 'software.amazon.awssdk', name: 'apigateway', version: '2.20.33' + testImplementation group: 'software.amazon.awssdk', name: 'sts', version: '2.20.33' testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version:'5.9.1' - testImplementation group: 'org.junit.vintage', name: 'junit-vintage-engine', version:'5.9.1' testImplementation group: 'org.testcontainers', name: 'testcontainers', version: '1.17.6' testImplementation group: 'org.testcontainers', name: 'junit-jupiter', version: '1.17.6' testImplementation group: 'org.testcontainers', name: 'localstack', version: '1.17.6' - testImplementation group: 'com.amazonaws', name: 'aws-java-sdk', version: '1.12.370' - testImplementation group: 'org.mock-server', name: 'mockserver-netty', version: '5.14.0' + testImplementation group: 'com.amazonaws', name: 'aws-java-sdk', version: '1.12.436' + testImplementation group: 'org.mock-server', name: 'mockserver-netty', version: '5.15.0' } compileJava { @@ -63,14 +87,13 @@ nativeImage { test { failFast = true - useJUnitPlatform() - exclude 'com/formkiq/stacks/api/awstest/**' + useJUnitPlatform() } -task testaws(type: Test) { - description = 'Runs AWS integration tests.' - include 'com/formkiq/stacks/api/awstest/**' - outputs.upToDateWhen {false} +task integrationTest(type: Test) { + testClassesDirs = sourceSets.integration.output.classesDirs + classpath = sourceSets.integration.runtimeClasspath + useJUnitPlatform() } task buildZip(type: Zip) { @@ -97,7 +120,7 @@ task buildJava11Zip(type: Zip) { task assembleTemplate { dependsOn buildZip - inputs.files("src/main/resources/cloudformation/template-snippet.yaml", "src/main/resources/cloudformation/api.yaml", "src/main/resources/cloudformation/api-iam.yaml") + inputs.files("src/main/resources/cloudformation/template.yaml", "src/main/resources/cloudformation/template-ocr.yaml", "src/main/resources/cloudformation/template-apikey.yaml", "src/main/resources/cloudformation/api.yaml", "src/main/resources/cloudformation/api-iam.yaml", "src/main/resources/cloudformation/api-apikey.yaml") outputs.dir("${buildDir}/distributions/formkiq-core") def randomtext = ('0'..'z').shuffled().take(10).join(); @@ -107,31 +130,44 @@ task assembleTemplate { copy { from layout.buildDirectory.file("${buildDir}/lambda-api-graalvm.zip") + from layout.buildDirectory.file("${buildDir}/layer-tesseract-5.3.1.zip") into "${buildDir}/distributions/formkiq-core/sam/api" } // build SAM distribution exec { - commandLine "bash", "-c", "ytt --data-value hash=${sha256} -f src/main/resources/cloudformation/template-snippet.yaml -f src/main/resources/cloudformation/api.yaml -f src/main/resources/cloudformation/api-iam.yaml > ${buildDir}/distributions/formkiq-core/sam/api/template.yaml" + commandLine getCmd(), getCmdParam(), "ytt --data-value hash=${sha256} --data-value version=${project.version} -f src/main/resources/cloudformation/template.yaml -f src/main/resources/cloudformation/template-ocr.yaml -f src/main/resources/cloudformation/template-apikey.yaml -f src/main/resources/cloudformation/api.yaml -f src/main/resources/cloudformation/api-iam.yaml -f src/main/resources/cloudformation/api-apikey.yaml > ${buildDir}/distributions/formkiq-core/sam/api/template.yaml" } } } +task downloadOpenApiGenerator(type: Download) { + dependsOn check + src "https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/6.6.0/openapi-generator-cli-6.6.0.jar" + dest buildDir + overwrite false +} + task assembleOpenApiTemplate { - inputs.files("src/main/resources/cloudformation/api.yaml", "src/main/resources/cloudformation/api-iam.yaml", "src/main/resources/cloudformation/api-variables.yaml") + dependsOn downloadOpenApiGenerator + inputs.files("src/main/resources/cloudformation/resources.yaml", "src/main/resources/cloudformation/api.yaml", "src/main/resources/cloudformation/api-iam.yaml", "src/main/resources/cloudformation/api-variables.yaml", "src/main/resources/cloudformation/api-iam-variables.yaml", "src/main/resources/cloudformation/api-key-variables.yaml") outputs.files("${project.projectDir}/openapi-jwt.yaml", "${project.projectDir}/openapi-iam.yaml") doLast { exec { - commandLine "bash", "-c", "ytt -f src/main/resources/cloudformation/resources.yaml -f src/main/resources/cloudformation/api.yaml -f src/main/resources/cloudformation/api-variables.yaml > ${buildDir}/openapi-jwt.yaml" + commandLine getCmd(), getCmdParam(), "ytt --data-value version=${project.version} -f src/main/resources/cloudformation/resources.yaml -f src/main/resources/cloudformation/api.yaml -f src/main/resources/cloudformation/api-variables.yaml > ${buildDir}/openapi-jwt.yaml" } exec { - commandLine "bash", "-c", "ytt -f src/main/resources/cloudformation/resources.yaml -f src/main/resources/cloudformation/api-iam.yaml -f src/main/resources/cloudformation/api-iam-variables.yaml > ${buildDir}/openapi-iam.yaml" + commandLine getCmd(), getCmdParam(), "ytt --data-value version=${project.version} -f src/main/resources/cloudformation/resources.yaml -f src/main/resources/cloudformation/api-iam.yaml -f src/main/resources/cloudformation/api-iam-variables.yaml > ${buildDir}/openapi-iam.yaml" } - for (fileName in ["openapi-jwt", "openapi-iam"]) { + exec { + commandLine getCmd(), getCmdParam(), "ytt --data-value version=${project.version} -f src/main/resources/cloudformation/resources.yaml -f src/main/resources/cloudformation/api-apikey.yaml -f src/main/resources/cloudformation/api-key-variables.yaml > ${buildDir}/openapi-key.yaml" + } + + for (fileName in ["openapi-jwt", "openapi-iam", "openapi-key"]) { def lines = new File("${buildDir}/${fileName}.yaml").readLines() for (int i = 0; i < 5; i++) { @@ -143,9 +179,16 @@ task assembleOpenApiTemplate { def file = new File("${buildDir}/../../docs/openapi/${fileName}.yaml") file.text = lines.join(System.lineSeparator()) - exec { - commandLine "bash", "-c", "openapi-generator generate -i ${buildDir}/../../docs/openapi/${fileName}.yaml -g asciidoc -o ${buildDir}/${fileName}" - } + javaexec { + main="-jar"; + args = [ + "${buildDir}/openapi-generator-cli-6.6.0.jar", + "generate", + "-i", "${buildDir}/../../docs/openapi/${fileName}.yaml", + "-g", "asciidoc", + "-o", "${buildDir}/${fileName}" + ] + } } } } diff --git a/lambda-api/config/checkstyle/import-control.xml b/lambda-api/config/checkstyle/import-control.xml index 1d29db69e..8b003360d 100644 --- a/lambda-api/config/checkstyle/import-control.xml +++ b/lambda-api/config/checkstyle/import-control.xml @@ -43,7 +43,7 @@ - + @@ -79,8 +79,9 @@ - + + diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/awstest/AbstractApiTest.java b/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/AbstractApiTest.java similarity index 90% rename from lambda-api/src/test/java/com/formkiq/stacks/api/awstest/AbstractApiTest.java rename to lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/AbstractApiTest.java index bba7cdc8e..fbc322ca7 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/awstest/AbstractApiTest.java +++ b/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/AbstractApiTest.java @@ -25,8 +25,8 @@ import static com.formkiq.aws.dynamodb.SiteIdKeyGenerator.DEFAULT_SITE_ID; import static com.formkiq.aws.dynamodb.objects.Objects.notNull; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; @@ -39,16 +39,15 @@ import java.net.http.HttpResponse.BodyHandlers; import java.nio.charset.StandardCharsets; import java.util.Arrays; +import java.util.HashMap; import java.util.List; import java.util.Map; -import org.junit.BeforeClass; +import org.junit.jupiter.api.BeforeAll; import com.formkiq.aws.cognito.CognitoConnectionBuilder; import com.formkiq.aws.cognito.CognitoService; import com.formkiq.aws.dynamodb.DynamoDbConnectionBuilder; import com.formkiq.aws.iam.IamConnectionBuilder; import com.formkiq.aws.iam.IamService; -import com.formkiq.aws.services.lambda.services.ConfigService; -import com.formkiq.aws.services.lambda.services.ConfigServiceImpl; import com.formkiq.aws.ssm.SsmConnectionBuilder; import com.formkiq.aws.ssm.SsmService; import com.formkiq.aws.ssm.SsmServiceImpl; @@ -61,6 +60,10 @@ import com.formkiq.stacks.client.requests.DeleteDocumentRequest; import com.formkiq.stacks.client.requests.GetDocumentRequest; import com.formkiq.stacks.client.requests.GetDocumentUploadRequest; +import com.formkiq.stacks.dynamodb.ApiKeysService; +import com.formkiq.stacks.dynamodb.ApiKeysServiceDynamoDb; +import com.formkiq.stacks.dynamodb.ConfigService; +import com.formkiq.stacks.dynamodb.ConfigServiceDynamoDb; import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.cognitoidentityprovider.model.AdminGetUserResponse; @@ -85,8 +88,12 @@ public abstract class AbstractApiTest { private static CognitoService adminCognitoService; /** {@link AuthenticationResultTypes}. */ private static AuthenticationResultType adminToken; + /** FormKiQ KEY API Client. */ + private static Map apiClient = new HashMap<>(); /** Api Gateway Invoke Group. */ private static String apiGatewayInvokeGroup; + /** {@link ConfigService}. */ + private static ApiKeysService apiKeysService; /** App Environment Name. */ private static String appenvironment; /** AWS Region. */ @@ -113,6 +120,8 @@ public abstract class AbstractApiTest { private static FormKiqClientV1 restClient; /** API Root Http Url. */ private static String rootHttpUrl; + /** Key API Root Url. */ + private static String rootKeyUrl; /** API Root Rest Url. */ private static String rootRestUrl; /** {@link SsmConnectionBuilder}. */ @@ -161,7 +170,7 @@ private static void addAndLoginCognito(final String username, final String group * * @throws IOException IOException */ - @BeforeClass + @BeforeAll public static void beforeClass() throws IOException { awsregion = Region.of(System.getProperty("testregion")); @@ -222,6 +231,29 @@ public static AuthenticationResultType getAdminToken() { return adminToken; } + /** + * Add API Key. + * + * @param siteId {@link String} + * @return {@link FormKiqClientV1} + */ + private static FormKiqClientV1 getApiKeyClient(final String siteId) { + + String site = siteId != null ? siteId : DEFAULT_SITE_ID; + if (!apiClient.containsKey(site)) { + String apiKey = apiKeysService.createApiKey(siteId, "My API Key", "testuser"); + + FormKiqClientConnection connection = new FormKiqClientConnection(rootKeyUrl) + .cognitoIdToken(apiKey).header("Origin", Arrays.asList("http://localhost")) + .header("Access-Control-Request-Method", Arrays.asList("GET")); + + FormKiqClientV1 client = new FormKiqClientV1(connection); + apiClient.put(site, client); + } + + return apiClient.get(site); + } + /** * Get App Environment. * @@ -243,10 +275,12 @@ protected static ConfigService getConfigService() { /** * Get FormKiq Clients. * + * @param siteId {@link String} + * * @return {@link List} {@link FormKiqClient} */ - public static List getFormKiqClients() { - return Arrays.asList(httpClient, restClient); + public static List getFormKiqClients(final String siteId) { + return Arrays.asList(httpClient, restClient, getApiKeyClient(siteId)); } /** @@ -284,7 +318,7 @@ public static boolean isIamAuthentication(final String url) { */ private static void loadSsmParameterVariables(final String awsprofile) { - ssmBuilder = new SsmConnectionBuilder().setCredentials(awsprofile).setRegion(awsregion); + ssmBuilder = new SsmConnectionBuilder(false).setCredentials(awsprofile).setRegion(awsregion); ssmService = new SsmServiceImpl(ssmBuilder); rootHttpUrl = @@ -293,6 +327,9 @@ private static void loadSsmParameterVariables(final String awsprofile) { rootRestUrl = ssmService.getParameterValue("/formkiq/" + appenvironment + "/api/DocumentsIamUrl"); + rootKeyUrl = + ssmService.getParameterValue("/formkiq/" + appenvironment + "/api/DocumentsKeyUrl"); + cognitoUserPoolId = ssmService.getParameterValue("/formkiq/" + appenvironment + "/cognito/UserPoolId"); @@ -370,8 +407,9 @@ private static void setupConfigService(final String awsprofile) { .getParameterValue("/formkiq/" + appenvironment + "/dynamodb/DocumentsTableName"); dbConnection = - new DynamoDbConnectionBuilder().setCredentials(awsprofile).setRegion(awsregion); - configService = new ConfigServiceImpl(dbConnection, documentsTable); + new DynamoDbConnectionBuilder(false).setCredentials(awsprofile).setRegion(awsregion); + configService = new ConfigServiceDynamoDb(dbConnection, documentsTable); + apiKeysService = new ApiKeysServiceDynamoDb(dbConnection, documentsTable); } } diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/awstest/AwsResourceTest.java b/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/AwsResourceTest.java similarity index 84% rename from lambda-api/src/test/java/com/formkiq/stacks/api/awstest/AwsResourceTest.java rename to lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/AwsResourceTest.java index ba78d1b37..49e4fff23 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/awstest/AwsResourceTest.java +++ b/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/AwsResourceTest.java @@ -23,10 +23,11 @@ */ package com.formkiq.stacks.api.awstest; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.net.URISyntaxException; import java.net.http.HttpResponse; @@ -40,7 +41,8 @@ import java.util.Map; import java.util.UUID; import java.util.concurrent.TimeUnit; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; import com.formkiq.aws.cognito.CognitoConnectionBuilder; import com.formkiq.aws.cognito.CognitoService; import com.formkiq.stacks.client.FormKiqClientV1; @@ -65,7 +67,8 @@ public class AwsResourceTest extends AbstractApiTest { /** * Test Having Admin add new user to group. */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testAdminAddUserToGroup() { // given String email = UUID.randomUUID() + "@formkiq.com"; @@ -99,7 +102,8 @@ public void testAdminAddUserToGroup() { * @throws URISyntaxException URISyntaxException * @throws InterruptedException InterruptedException */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testApiDocuments01() throws IOException, URISyntaxException, InterruptedException { // given final int year = 2019; @@ -107,7 +111,7 @@ public void testApiDocuments01() throws IOException, URISyntaxException, Interru final int day = 15; LocalDate localDate = LocalDate.of(year, month, day); - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { // given Date date = Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant()); GetDocumentsRequest request = new GetDocumentsRequest().date(date).tz("+0500"); @@ -132,9 +136,10 @@ public void testApiDocuments01() throws IOException, URISyntaxException, Interru * @throws Exception Exception */ @SuppressWarnings("unchecked") - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testPresignedUrl01() throws Exception { - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { // given final String documentId = addDocumentWithoutFile(client, null, null); @@ -175,7 +180,8 @@ public void testPresignedUrl01() throws Exception { /** * Test SSM Parameter Store. */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testSsmParameters() { assertTrue(getParameterStoreValue("/formkiq/" + getAppenvironment() + "/api/DocumentsHttpUrl") .endsWith(getAwsRegion() + ".amazonaws.com")); @@ -194,24 +200,39 @@ public void testSsmParameters() { /** * Test Having User try and add itself to the admin group. Should fail. */ - @Test(expected = NotAuthorizedException.class) + @Test public void testUserAddSelfToAdmin() { - getAdminCognitoService().getCredentials(login(USER_EMAIL, USER_PASSWORD)); + try { + getAdminCognitoService().getCredentials(login(USER_EMAIL, USER_PASSWORD)); + fail(); + } catch (NotAuthorizedException e) { + assertTrue(true); + } } /** * Test Having User try and add itself to the admin group. Should fail. */ - @Test(expected = NotAuthorizedException.class) + @Test public void testFinanceUserAddSelfToAdmin() { - getAdminCognitoService().getCredentials(login(FINANCE_EMAIL, USER_PASSWORD)); + try { + getAdminCognitoService().getCredentials(login(FINANCE_EMAIL, USER_PASSWORD)); + fail(); + } catch (NotAuthorizedException e) { + assertTrue(true); + } } /** * Test Having User try and add itself to the admin group. Should fail. */ - @Test(expected = NotAuthorizedException.class) + @Test public void testReadonlyUserAddSelfToAdmin() { - getAdminCognitoService().getCredentials(login(READONLY_EMAIL, USER_PASSWORD)); + try { + getAdminCognitoService().getCredentials(login(READONLY_EMAIL, USER_PASSWORD)); + fail(); + } catch (NotAuthorizedException e) { + assertTrue(true); + } } } diff --git a/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/ChatGptRequestTest.java b/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/ChatGptRequestTest.java new file mode 100644 index 000000000..dc3653ce3 --- /dev/null +++ b/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/ChatGptRequestTest.java @@ -0,0 +1,124 @@ +/** + * MIT License + * + * Copyright (c) 2018 - 2020 FormKiQ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.formkiq.stacks.api.awstest; + +import static com.formkiq.testutils.aws.FkqDocumentService.addDocument; +import static com.formkiq.testutils.aws.FkqDocumentService.waitForActionsComplete; +import static com.formkiq.testutils.aws.FkqDocumentService.waitForDocumentContent; +import static org.junit.jupiter.api.Assertions.assertTrue; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import com.formkiq.stacks.client.FormKiqClientV1; +import com.formkiq.stacks.client.models.AddDocumentAction; +import com.formkiq.stacks.client.models.Configuration; +import com.formkiq.stacks.client.models.DocumentActionType; +import com.formkiq.stacks.client.models.DocumentTags; +import com.formkiq.stacks.client.requests.AddDocumentActionRequest; +import com.formkiq.stacks.client.requests.GetDocumentTagsRequest; +import com.formkiq.stacks.client.requests.UpdateConfigurationRequest; +import software.amazon.awssdk.utils.IoUtils; + +/** + * + * Integration Test for creating an OCR / ChatGPT actions. + * + */ +public class ChatGptRequestTest extends AbstractApiTest { + + /** JUnit Test Timeout. */ + private static final int TEST_TIMEOUT = 120000; + + @BeforeAll + public static void beforeClass() throws IOException { + AbstractApiTest.beforeClass(); + + if (System.getProperty("testchatgptapikey") == null) { + throw new IOException("key not set"); + } + + FormKiqClientV1 client = getFormKiqClients(null).get(0); + Configuration config = new Configuration(); + config.chatGptApiKey(System.getProperty("testchatgptapikey")); + try { + client.updateConfiguration(new UpdateConfigurationRequest().config(config)); + } catch (InterruptedException e) { + throw new IOException(e); + } + } + + /** + * POST Document OCR using Actions. + * + * @throws Exception Exception + */ + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) + public void testOcrAndChatGpt01() throws Exception { + + // given + for (String siteId : Arrays.asList(null, UUID.randomUUID().toString())) { + + for (FormKiqClientV1 client : getFormKiqClients(siteId)) { + + byte[] content = toBytes("/ocr/receipt.png"); + String documentId = addDocument(client, siteId, "receipt.png", content, "image/png"); + waitForDocumentContent(client, siteId, documentId); + + // when + AddDocumentActionRequest addReq = + new AddDocumentActionRequest().siteId(siteId).documentId(documentId) + .actions(Arrays.asList(new AddDocumentAction().type(DocumentActionType.OCR), + new AddDocumentAction().type(DocumentActionType.DOCUMENTTAGGING) + .parameters(Map.of("engine", "chatgpt", "tags", + "organization,location,person,subject,sentiment,document type")))); + client.addDocumentAction(addReq); + + // then + waitForActionsComplete(client, siteId, documentId, DocumentActionType.DOCUMENTTAGGING); + + GetDocumentTagsRequest tagsReq = + new GetDocumentTagsRequest().siteId(siteId).documentId(documentId); + DocumentTags tags = client.getDocumentTags(tagsReq); + assertTrue( + tags.tags().stream().filter(r -> r.key().equals("untagged")).findFirst().isEmpty()); + + assertTrue(tags.tags().stream().filter(r -> r.key().toLowerCase().equals("person")) + .findFirst().isPresent()); + } + } + } + + private byte[] toBytes(final String name) throws IOException { + try (InputStream is = getClass().getResourceAsStream(name)) { + return IoUtils.toByteArray(is); + } + } +} diff --git a/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/ConfigurationApiKeyRequestTest.java b/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/ConfigurationApiKeyRequestTest.java new file mode 100644 index 000000000..5f3e77ea3 --- /dev/null +++ b/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/ConfigurationApiKeyRequestTest.java @@ -0,0 +1,121 @@ +/** + * MIT License + * + * Copyright (c) 2018 - 2020 FormKiQ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.formkiq.stacks.api.awstest; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import java.net.http.HttpResponse; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import com.formkiq.stacks.client.FormKiqClientV1; +import com.formkiq.stacks.client.models.ApiKeys; +import com.formkiq.stacks.client.requests.AddApiKeyRequest; +import software.amazon.awssdk.services.cognitoidentityprovider.model.AuthenticationResultType; + +/** + * Process Urls. + *

+ * GET /configuration/apiKeys integration tests + *

+ * + */ +public class ConfigurationApiKeyRequestTest extends AbstractApiTest { + + /** JUnit Test Timeout. */ + private static final int TEST_TIMEOUT = 20000; + + /** + * Test GET /configuration/apiKeys. + * + * @throws Exception Exception + */ + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) + public void testApiKey01() throws Exception { + // given + String name = "My API"; + final int expected = 3; + + List clients = getFormKiqClients(null); + assertEquals(expected, clients.size()); + + for (String siteId : Arrays.asList(null, UUID.randomUUID().toString())) { + + for (FormKiqClientV1 client : Arrays.asList(clients.get(0), clients.get(1))) { + AddApiKeyRequest req = new AddApiKeyRequest().siteId(siteId).name(name); + + // when + client.addApiKey(req); + + // then + ApiKeys apiKeys = client.getApiKeys(); + assertFalse(apiKeys.apiKeys().isEmpty()); + } + + // given + FormKiqClientV1 c = clients.get(2); + + // when + HttpResponse response = c.getApiKeysAsHttpResponse(); + + // then + assertEquals("401", String.valueOf(response.statusCode())); + assertEquals("{\"message\":\"user is unauthorized\"}", response.body()); + } + } + + /** + * Test GET /configuration/apiKeys as readuser user. + * + * @throws Exception Exception + */ + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) + public void testApiKey02() throws Exception { + // given + AuthenticationResultType token = login(FINANCE_EMAIL, USER_PASSWORD); + FormKiqClientV1 client = createHttpClient(token); + + // when + HttpResponse response = client.getApiKeysAsHttpResponse(); + + // then + assertEquals("401", String.valueOf(response.statusCode())); + assertEquals("{\"message\":\"user is unauthorized\"}", response.body()); + + // given + AddApiKeyRequest req = new AddApiKeyRequest().name("test"); + + // when + response = client.addApiKeyAsHttpResponse(req); + + // then + assertEquals("401", String.valueOf(response.statusCode())); + assertEquals("{\"message\":\"user is unauthorized\"}", response.body()); + } +} diff --git a/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/ConfigurationRequestTest.java b/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/ConfigurationRequestTest.java new file mode 100644 index 000000000..00ff1c512 --- /dev/null +++ b/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/ConfigurationRequestTest.java @@ -0,0 +1,103 @@ +/** + * MIT License + * + * Copyright (c) 2018 - 2020 FormKiQ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.formkiq.stacks.api.awstest; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import java.net.http.HttpResponse; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import com.formkiq.stacks.client.FormKiqClientV1; +import com.formkiq.stacks.client.models.Configuration; +import software.amazon.awssdk.services.cognitoidentityprovider.model.AuthenticationResultType; + +/** + * Process Urls. + *

+ * GET /configuration tests + *

+ * + */ +public class ConfigurationRequestTest extends AbstractApiTest { + + /** JUnit Test Timeout. */ + private static final int TEST_TIMEOUT = 20000; + + /** + * Test GET /configuration. + * + * @throws Exception Exception + */ + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) + public void testConfigs01() throws Exception { + + // given + final int expected = 3; + List clients = getFormKiqClients(null); + assertEquals(expected, clients.size()); + + for (FormKiqClientV1 client : Arrays.asList(clients.get(0), clients.get(1))) { + + // when + Configuration c = client.getConfiguration(); + + // then + assertNotNull(c.chatGptApiKey()); + } + + // given + FormKiqClientV1 fc = clients.get(2); + + // when + HttpResponse response = fc.getConfigurationAsHttpResponse(); + + // then + assertEquals("401", String.valueOf(response.statusCode())); + assertEquals("{\"message\":\"user is unauthorized\"}", response.body()); + } + + /** + * Test GET /configuration as readuser user. + * + * @throws Exception Exception + */ + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) + public void testConfigs02() throws Exception { + // given + AuthenticationResultType token = login(FINANCE_EMAIL, USER_PASSWORD); + FormKiqClientV1 client = createHttpClient(token); + + // when + HttpResponse response = client.getConfigurationAsHttpResponse(); + + // then + assertEquals("401", String.valueOf(response.statusCode())); + assertEquals("{\"message\":\"user is unauthorized\"}", response.body()); + } +} diff --git a/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/DocumentsDocumentIdOcrRequestTest.java b/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/DocumentsDocumentIdOcrRequestTest.java new file mode 100644 index 000000000..770abc64b --- /dev/null +++ b/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/DocumentsDocumentIdOcrRequestTest.java @@ -0,0 +1,156 @@ +/** + * MIT License + * + * Copyright (c) 2018 - 2020 FormKiQ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.formkiq.stacks.api.awstest; + +import static com.formkiq.testutils.aws.FkqDocumentService.addDocument; +import static com.formkiq.testutils.aws.FkqDocumentService.waitForDocumentContent; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import com.formkiq.stacks.client.FormKiqClient; +import com.formkiq.stacks.client.FormKiqClientV1; +import com.formkiq.stacks.client.models.AddDocumentAction; +import com.formkiq.stacks.client.models.DocumentActionType; +import com.formkiq.stacks.client.models.DocumentActions; +import com.formkiq.stacks.client.models.DocumentOcr; +import com.formkiq.stacks.client.requests.AddDocumentActionRequest; +import com.formkiq.stacks.client.requests.AddDocumentOcrRequest; +import com.formkiq.stacks.client.requests.GetDocumentActionsRequest; +import com.formkiq.stacks.client.requests.GetDocumentOcrRequest; +import software.amazon.awssdk.utils.IoUtils; + +/** + * GET, OPTIONS, POST, PUT, DELETE /documents/{documentId}/ocr tests. + * + */ +public class DocumentsDocumentIdOcrRequestTest extends AbstractApiTest { + + /** JUnit Test Timeout. */ + private static final int TEST_TIMEOUT = 90000; + + /** + * POST Document OCR. + * + * @throws Exception Exception + */ + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) + public void testAddOcr01() throws Exception { + + // given + for (String siteId : Arrays.asList(null, UUID.randomUUID().toString())) { + for (FormKiqClientV1 client : getFormKiqClients(siteId)) { + + byte[] content = toBytes("/ocr/receipt.png"); + String documentId = addDocument(client, siteId, "receipt.png", content, "image/png"); + waitForDocumentContent(client, siteId, documentId); + + // when + AddDocumentOcrRequest addReq = + new AddDocumentOcrRequest().siteId(siteId).documentId(documentId); + client.addDocumentOcr(addReq); + + // then + DocumentOcr documentOcr = getDocumentOcr(client, siteId, documentId); + assertTrue(documentOcr.data().contains("East Repair")); + } + } + } + + /** + * POST Document OCR using Actions. + * + * @throws Exception Exception + */ + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) + public void testAddOcr02() throws Exception { + + // given + for (String siteId : Arrays.asList(null, UUID.randomUUID().toString())) { + for (FormKiqClientV1 client : getFormKiqClients(siteId)) { + + byte[] content = toBytes("/ocr/receipt.png"); + String documentId = addDocument(client, siteId, "receipt.png", content, "image/png"); + waitForDocumentContent(client, siteId, documentId); + + // when + AddDocumentActionRequest addReq = + new AddDocumentActionRequest().siteId(siteId).documentId(documentId) + .actions(Arrays.asList(new AddDocumentAction().type(DocumentActionType.OCR))); + client.addDocumentAction(addReq); + + // then + DocumentOcr documentOcr = getDocumentOcr(client, siteId, documentId); + assertTrue(documentOcr.data().contains("East Repair")); + + GetDocumentActionsRequest getReq = + new GetDocumentActionsRequest().siteId(siteId).documentId(documentId); + DocumentActions actions = client.getDocumentActions(getReq); + assertEquals(1, actions.actions().size()); + assertEquals("complete", actions.actions().get(0).status()); + } + } + } + + /** + * Wait for {@link DocumentOcr} to have data. + * + * @param client {@link FormKiqClient} + * @param siteId {@link String} + * @param documentId {@link String} + * @return {@link DocumentOcr} + * @throws IOException IOException + * @throws InterruptedException InterruptedException + */ + private DocumentOcr getDocumentOcr(final FormKiqClient client, final String siteId, + final String documentId) throws IOException, InterruptedException { + DocumentOcr documentOcr = null; + GetDocumentOcrRequest getReq = + new GetDocumentOcrRequest().siteId(siteId).documentId(documentId); + + while (documentOcr == null || documentOcr.data() == null) { + try { + documentOcr = client.getDocumentOcr(getReq); + } catch (IOException e) { + // ignore + } + TimeUnit.SECONDS.sleep(1); + } + + return documentOcr; + } + + private byte[] toBytes(final String name) throws IOException { + try (InputStream is = getClass().getResourceAsStream(name)) { + return IoUtils.toByteArray(is); + } + } +} diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/awstest/DocumentsDocumentIdSyncsRequestTest.java b/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/DocumentsDocumentIdSyncsRequestTest.java similarity index 93% rename from lambda-api/src/test/java/com/formkiq/stacks/api/awstest/DocumentsDocumentIdSyncsRequestTest.java rename to lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/DocumentsDocumentIdSyncsRequestTest.java index 51c2a122c..7b5098696 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/awstest/DocumentsDocumentIdSyncsRequestTest.java +++ b/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/DocumentsDocumentIdSyncsRequestTest.java @@ -33,7 +33,8 @@ import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; import com.formkiq.aws.dynamodb.model.DocumentSyncServiceType; import com.formkiq.aws.dynamodb.model.DocumentSyncStatus; import com.formkiq.aws.dynamodb.model.DocumentSyncType; @@ -74,17 +75,18 @@ private boolean isComplete(final String formkiqType, final DocumentSyncs syncs) * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testGetSyncs01() throws Exception { String formkiqType = null; - for (FormKiqClientV1 client : getFormKiqClients()) { - // given - Version version = client.getVersion(); - formkiqType = version.type(); + // given + for (String siteId : Arrays.asList(null, UUID.randomUUID().toString())) { + for (FormKiqClientV1 client : getFormKiqClients(siteId)) { - for (String siteId : Arrays.asList(null, UUID.randomUUID().toString())) { + Version version = client.getVersion(); + formkiqType = version.type(); String path = UUID.randomUUID().toString(); String documentId = addDocumentWithoutFile(client, siteId, path); diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/awstest/DocumentsDocumentIdTagsRequestTest.java b/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/DocumentsDocumentIdTagsRequestTest.java similarity index 93% rename from lambda-api/src/test/java/com/formkiq/stacks/api/awstest/DocumentsDocumentIdTagsRequestTest.java rename to lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/DocumentsDocumentIdTagsRequestTest.java index 20f032a4e..63cddced9 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/awstest/DocumentsDocumentIdTagsRequestTest.java +++ b/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/DocumentsDocumentIdTagsRequestTest.java @@ -23,17 +23,19 @@ */ package com.formkiq.stacks.api.awstest; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.net.URISyntaxException; import java.net.http.HttpResponse; import java.util.Arrays; import java.util.List; import java.util.Map; -import org.junit.Test; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; import com.formkiq.stacks.client.FormKiqClientV1; import com.formkiq.stacks.client.models.AddDocumentTag; import com.formkiq.stacks.client.requests.AddDocumentTagRequest; @@ -85,11 +87,11 @@ private void deleteDocumentTag(final FormKiqClientV1 client, final String docume * @throws Exception Exception */ @SuppressWarnings("unchecked") - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testDocumentsTags01() throws Exception { - for (FormKiqClientV1 client : getFormKiqClients()) { - + for (FormKiqClientV1 client : getFormKiqClients(null)) { // given String documentId = addDocumentWithoutFile(client, null, null); AddDocumentTagRequest request = @@ -138,10 +140,11 @@ public void testDocumentsTags01() throws Exception { * @throws Exception Exception */ @SuppressWarnings("unchecked") - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testDocumentsTags02() throws Exception { - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { // given String documentId = addDocumentWithoutFile(client, null, null); @@ -191,10 +194,11 @@ public void testDocumentsTags02() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testDocumentsTags03() throws Exception { - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { // given String documentId = addDocumentWithoutFile(client, null, null); @@ -268,10 +272,11 @@ public void testDocumentsTags03() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testDocumentsTags04() throws Exception { - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { // given String documentId = addDocumentWithoutFile(client, null, null); @@ -346,10 +351,11 @@ public void testDocumentsTags04() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testDocumentsTags05() throws Exception { - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { // given String documentId = addDocumentWithoutFile(client, null, null); @@ -407,10 +413,11 @@ public void testDocumentsTags05() throws Exception { * @throws Exception Exception */ @SuppressWarnings("unchecked") - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testDocumentsTags06() throws Exception { - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { // given String documentId = addDocumentWithoutFile(client, null, null); @@ -479,8 +486,10 @@ public void testDocumentsTags06() throws Exception { private void verifyUserId(final Map map) { if ("testadminuser@formkiq.com".equals(map.get("userId"))) { assertEquals("testadminuser@formkiq.com", map.get("userId")); - } else { + } else if (map.get("userId").toString().contains(":user/")) { assertTrue(map.get("userId").toString().contains(":user/")); + } else { + assertEquals("My API Key", map.get("userId")); } } } diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/awstest/DocumentsDocumentIdUploadRequestTest.java b/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/DocumentsDocumentIdUploadRequestTest.java similarity index 85% rename from lambda-api/src/test/java/com/formkiq/stacks/api/awstest/DocumentsDocumentIdUploadRequestTest.java rename to lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/DocumentsDocumentIdUploadRequestTest.java index eaa0eaeb2..e4cc3f6a0 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/awstest/DocumentsDocumentIdUploadRequestTest.java +++ b/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/DocumentsDocumentIdUploadRequestTest.java @@ -23,12 +23,14 @@ */ package com.formkiq.stacks.api.awstest; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import java.net.http.HttpResponse; import java.util.Map; import java.util.UUID; -import org.junit.Test; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; import com.formkiq.stacks.client.FormKiqClientV1; import com.formkiq.stacks.client.requests.GetDocumentUploadRequest; import com.formkiq.stacks.client.requests.OptionsDocumentRequest; @@ -47,10 +49,11 @@ public class DocumentsDocumentIdUploadRequestTest extends AbstractApiTest { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testGet01() throws Exception { - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { // given String documentId = UUID.randomUUID().toString(); GetDocumentUploadRequest request = @@ -70,9 +73,10 @@ public void testGet01() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testGet02() throws Exception { - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { // given String documentId = addDocumentWithoutFile(client, null, null); GetDocumentUploadRequest request = @@ -96,9 +100,10 @@ public void testGet02() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testOptions01() throws Exception { - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { // given String documentId = UUID.randomUUID().toString(); OptionsDocumentRequest req = new OptionsDocumentRequest().documentId(documentId); diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/awstest/DocumentsDocumentIdUrlRequestTest.java b/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/DocumentsDocumentIdUrlRequestTest.java similarity index 89% rename from lambda-api/src/test/java/com/formkiq/stacks/api/awstest/DocumentsDocumentIdUrlRequestTest.java rename to lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/DocumentsDocumentIdUrlRequestTest.java index e223b0361..66f230e51 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/awstest/DocumentsDocumentIdUrlRequestTest.java +++ b/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/DocumentsDocumentIdUrlRequestTest.java @@ -23,8 +23,8 @@ */ package com.formkiq.stacks.api.awstest; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; @@ -32,7 +32,9 @@ import java.net.http.HttpResponse.BodyHandlers; import java.util.Map; import java.util.UUID; -import org.junit.Test; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; import com.formkiq.stacks.client.FormKiqClientV1; import com.formkiq.stacks.client.requests.GetDocumentContentUrlRequest; import com.formkiq.stacks.client.requests.OptionsDocumentUploadRequest; @@ -55,9 +57,10 @@ public class DocumentsDocumentIdUrlRequestTest extends AbstractApiTest { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testGet01() throws Exception { - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { // given String documentId = addDocumentWithoutFile(client, null, null); Thread.sleep(SLEEP * 2); @@ -70,10 +73,11 @@ public void testGet01() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testOptions01() throws Exception { - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { // given String documentId = UUID.randomUUID().toString(); OptionsDocumentUploadRequest req = new OptionsDocumentUploadRequest().documentId(documentId); diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/awstest/DocumentsDocumentIdVersionsRequestTest.java b/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/DocumentsDocumentIdVersionsRequestTest.java similarity index 87% rename from lambda-api/src/test/java/com/formkiq/stacks/api/awstest/DocumentsDocumentIdVersionsRequestTest.java rename to lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/DocumentsDocumentIdVersionsRequestTest.java index 57c1a2282..75b2385c8 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/awstest/DocumentsDocumentIdVersionsRequestTest.java +++ b/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/DocumentsDocumentIdVersionsRequestTest.java @@ -23,10 +23,12 @@ */ package com.formkiq.stacks.api.awstest; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.net.http.HttpResponse; import java.util.UUID; -import org.junit.Test; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; import com.formkiq.stacks.client.FormKiqClientV1; import com.formkiq.stacks.client.requests.OptionsDocumentVersionsRequest; @@ -44,10 +46,11 @@ public class DocumentsDocumentIdVersionsRequestTest extends AbstractApiTest { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testOptions01() throws Exception { - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { // given String documentId = UUID.randomUUID().toString(); OptionsDocumentVersionsRequest req = diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/awstest/DocumentsRequestTest.java b/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/DocumentsRequestTest.java similarity index 92% rename from lambda-api/src/test/java/com/formkiq/stacks/api/awstest/DocumentsRequestTest.java rename to lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/DocumentsRequestTest.java index 4c6f6b843..b74dd70fd 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/awstest/DocumentsRequestTest.java +++ b/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/DocumentsRequestTest.java @@ -24,13 +24,13 @@ package com.formkiq.stacks.api.awstest; import static com.formkiq.aws.dynamodb.SiteIdKeyGenerator.DEFAULT_SITE_ID; -import static com.formkiq.aws.services.lambda.services.ConfigService.MAX_DOCUMENTS; +import static com.formkiq.stacks.dynamodb.ConfigService.MAX_DOCUMENTS; import static com.formkiq.testutils.aws.FkqDocumentService.waitForDocumentContent; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; @@ -48,10 +48,13 @@ import java.util.Map; import java.util.Optional; import java.util.UUID; +import java.util.concurrent.TimeUnit; import java.util.function.BiPredicate; -import org.junit.AfterClass; -import org.junit.Test; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; import com.formkiq.aws.dynamodb.DynamicObject; +import com.formkiq.aws.dynamodb.objects.MimeType; import com.formkiq.stacks.client.FormKiqClient; import com.formkiq.stacks.client.FormKiqClientConnection; import com.formkiq.stacks.client.FormKiqClientV1; @@ -77,7 +80,6 @@ import com.formkiq.stacks.client.requests.OptionsDocumentRequest; import com.formkiq.stacks.client.requests.SearchDocumentsRequest; import com.formkiq.stacks.client.requests.UpdateDocumentRequest; -import com.formkiq.stacks.common.formats.MimeType; import software.amazon.awssdk.core.sync.RequestBody; import software.amazon.awssdk.services.cognitoidentityprovider.model.AuthenticationResultType; @@ -109,7 +111,7 @@ public class DocumentsRequestTest extends AbstractApiTest { /** * After Class. */ - @AfterClass + @AfterAll public static void afterClass() { getConfigService().delete(SITEID1); } @@ -172,9 +174,10 @@ private DocumentTags getDocumentTags(final FormKiqClient client, final String do * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testDelete01() throws Exception { - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { // given String documentId = UUID.randomUUID().toString(); DeleteDocumentRequest request = new DeleteDocumentRequest().documentId(documentId); @@ -193,9 +196,10 @@ public void testDelete01() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testGet01() throws Exception { - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { // given GetDocumentsRequest request = new GetDocumentsRequest().date(new Date()); // when @@ -213,7 +217,8 @@ public void testGet01() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testGet02() throws Exception { // given final String siteId = "finance"; @@ -249,7 +254,8 @@ public void testGet02() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testGet03() throws Exception { // given final String siteId = "finance"; @@ -282,7 +288,8 @@ public void testGet03() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testGet04() throws Exception { // given final String siteId = "finance"; @@ -309,7 +316,8 @@ public void testGet04() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testGet05() throws Exception { // given final String siteId = "finance"; @@ -337,9 +345,10 @@ public void testGet05() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testOptions01() throws Exception { - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { // given String documentId = UUID.randomUUID().toString(); OptionsDocumentRequest req = new OptionsDocumentRequest().documentId(documentId); @@ -358,9 +367,10 @@ public void testOptions01() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testOptions02() throws Exception { - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { // given // when HttpResponse response = client.optionsDocuments(); @@ -376,9 +386,10 @@ public void testOptions02() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testPost01() throws Exception { - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { // given AddDocument post = new AddDocument().contentType("text/plain").content("test data", StandardCharsets.UTF_8); @@ -466,7 +477,8 @@ private void testPost01Delete(final FormKiqClientV1 client, final String documen * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testPost02() throws Exception { // given final String siteId = "finance"; @@ -499,7 +511,8 @@ public void testPost02() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testPost03() throws Exception { // given final String siteId = "finance"; @@ -530,7 +543,8 @@ public void testPost03() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testPost04() throws Exception { // given final String siteId = "finance"; @@ -563,7 +577,8 @@ public void testPost04() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testPost05() throws Exception { // given final String siteId = "finance"; @@ -601,7 +616,8 @@ public void testPost05() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testPost06() throws Exception { // given getConfigService().save(SITEID1, new DynamicObject(Map.of(MAX_DOCUMENTS, "1"))); @@ -611,12 +627,12 @@ public void testPost06() throws Exception { post.contentType("application/pdf"); AddDocumentRequest req = new AddDocumentRequest().document(post).siteId(SITEID1); - FormKiqClientV1 c = getFormKiqClients().get(0); + FormKiqClientV1 c = getFormKiqClients(SITEID1).get(0); HttpResponse response = c.addDocumentAsHttpResponse(req); assertEquals(STATUS_CREATED, response.statusCode()); - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(SITEID1)) { // when response = client.addDocumentAsHttpResponse(req); @@ -632,12 +648,13 @@ public void testPost06() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testPost07() throws Exception { for (boolean enablePublicEndpoint : Arrays.asList(Boolean.FALSE, Boolean.TRUE)) { - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { // given AddDocument post = new AddDocument() .tags(Arrays.asList(new AddDocumentTag().key("formName").value("Job Application Form"))) @@ -714,10 +731,11 @@ public void testPost07() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testPost08() throws Exception { // given - FormKiqClientV1 client = getFormKiqClients().get(0); + FormKiqClientV1 client = getFormKiqClients(null).get(0); String url = getRootHttpUrl() + "/documents"; Map> headers = @@ -761,9 +779,10 @@ public boolean test(final String t, final String u) { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testPost09() throws Exception { - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { // given AddDocument post = new AddDocument().path("something.txt").contentType("text/plain") .content("test data", StandardCharsets.UTF_8) @@ -829,12 +848,13 @@ public void testPost09() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testPost10() throws Exception { // given String content = "test data"; for (String siteId : Arrays.asList(null, UUID.randomUUID().toString())) { - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(siteId)) { AddDocument post = new AddDocument().contentType("text/plain").content(content, StandardCharsets.UTF_8) diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/awstest/DocumentsSearchRequestTest.java b/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/DocumentsSearchRequestTest.java similarity index 88% rename from lambda-api/src/test/java/com/formkiq/stacks/api/awstest/DocumentsSearchRequestTest.java rename to lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/DocumentsSearchRequestTest.java index b519c9092..05909a134 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/awstest/DocumentsSearchRequestTest.java +++ b/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/DocumentsSearchRequestTest.java @@ -23,16 +23,17 @@ */ package com.formkiq.stacks.api.awstest; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.net.http.HttpResponse; import java.util.Arrays; import java.util.Optional; import java.util.UUID; import java.util.concurrent.TimeUnit; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; import com.formkiq.stacks.client.FormKiqClientV1; import com.formkiq.stacks.client.models.Document; import com.formkiq.stacks.client.models.DocumentSearchQuery; @@ -57,9 +58,10 @@ public class DocumentsSearchRequestTest extends AbstractApiTest { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testDocumentsRawSearch01() throws Exception { - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { // given SearchDocumentsRequest req = new SearchDocumentsRequest() .query(new DocumentSearchQuery().tag(new DocumentSearchTag().key("untagged"))); @@ -79,10 +81,11 @@ public void testDocumentsRawSearch01() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testDocumentsSearch01() throws Exception { // given - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { addDocumentWithoutFile(client, null, null); SearchDocumentsRequest req = new SearchDocumentsRequest() .query(new DocumentSearchQuery().tag(new DocumentSearchTag().key("untagged"))); @@ -105,9 +108,10 @@ public void testDocumentsSearch01() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testDocumentsSearch02() throws Exception { - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { // given String next = "3aa3a255-6a67-4d05-8e67-f7a22b827433"; SearchDocumentsRequest req = new SearchDocumentsRequest() @@ -131,10 +135,11 @@ public void testDocumentsSearch02() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testDocumentsSearch03() throws Exception { // given - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { String documentId = addDocumentWithoutFile(client, null, null); SearchDocumentsRequest req = new SearchDocumentsRequest().query(new DocumentSearchQuery() .tag(new DocumentSearchTag().key("untagged")).documentIds(Arrays.asList(documentId))); @@ -157,10 +162,11 @@ public void testDocumentsSearch03() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testDocumentsSearch04() throws Exception { // given - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { addDocumentWithoutFile(client, null, null); SearchDocumentsRequest req = new SearchDocumentsRequest() .query(new DocumentSearchQuery().tag(new DocumentSearchTag().key("untagged")) @@ -179,10 +185,11 @@ public void testDocumentsSearch04() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testDocumentsSearch05() throws Exception { // given - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { String documentId = addDocumentWithoutFile(client, null, null); AddDocumentTagRequest tagRequest = new AddDocumentTagRequest().documentId(documentId).tagKey("test").tagValue("somevalue"); @@ -223,10 +230,11 @@ public void testDocumentsSearch05() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testDocumentsSearch06() throws Exception { // given - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { String documentId = addDocumentWithoutFile(client, null, null); AddDocumentTagRequest tagRequest = new AddDocumentTagRequest().documentId(documentId).tagKey("test").tagValue("somevalue"); @@ -264,10 +272,11 @@ public void testDocumentsSearch06() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testDocumentsSearch07() throws Exception { // given - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { String tagKey = UUID.randomUUID().toString(); String documentId = addDocumentWithoutFile(client, null, null); AddDocumentTagRequest tagRequest = @@ -304,14 +313,15 @@ public void testDocumentsSearch07() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testDocumentsSearch08() throws Exception { // given String siteId = null; String path = "some/thing/else/My Documents.pdf"; String text = "My Documents"; final int limit = 100; - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { String documentId = addDocumentWithoutFile(client, siteId, path); SearchDocumentsRequest req = new SearchDocumentsRequest().siteId(siteId) diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/awstest/DocumentsUploadRequestTest.java b/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/DocumentsUploadRequestTest.java similarity index 86% rename from lambda-api/src/test/java/com/formkiq/stacks/api/awstest/DocumentsUploadRequestTest.java rename to lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/DocumentsUploadRequestTest.java index 41cd00a41..9ba6e819a 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/awstest/DocumentsUploadRequestTest.java +++ b/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/DocumentsUploadRequestTest.java @@ -23,10 +23,10 @@ */ package com.formkiq.stacks.api.awstest; -import static com.formkiq.aws.services.lambda.services.ConfigService.MAX_DOCUMENTS; -import static com.formkiq.aws.services.lambda.services.ConfigService.MAX_DOCUMENT_SIZE_BYTES; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static com.formkiq.stacks.dynamodb.ConfigService.MAX_DOCUMENTS; +import static com.formkiq.stacks.dynamodb.ConfigService.MAX_DOCUMENT_SIZE_BYTES; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import java.io.IOException; import java.net.URI; import java.net.http.HttpClient; @@ -37,10 +37,13 @@ import java.util.Arrays; import java.util.Map; import java.util.UUID; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.Test; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; import com.formkiq.aws.dynamodb.DynamicObject; +import com.formkiq.aws.dynamodb.objects.MimeType; import com.formkiq.stacks.client.FormKiqClientV1; import com.formkiq.stacks.client.models.AddLargeDocument; import com.formkiq.stacks.client.models.DocumentContent; @@ -48,7 +51,6 @@ import com.formkiq.stacks.client.requests.AddLargeDocumentRequest; import com.formkiq.stacks.client.requests.GetDocumentContentRequest; import com.formkiq.stacks.client.requests.GetDocumentUploadRequest; -import com.formkiq.stacks.common.formats.MimeType; /** * GET, OPTIONS /documents/upload tests. @@ -75,7 +77,7 @@ public class DocumentsUploadRequestTest extends AbstractApiTest { /** * After Class. */ - @AfterClass + @AfterAll public static void afterClass() { getConfigService().delete(SITEID0); getConfigService().delete(SITEID1); @@ -109,7 +111,7 @@ private void assertDocumentContent(final FormKiqClientV1 client, final String do /** * before. */ - @Before + @BeforeEach public void before() { getConfigService().delete(SITEID0); getConfigService().delete(SITEID1); @@ -120,9 +122,10 @@ public void before() { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testGet01() throws Exception { - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { // given String content = "test content"; GetDocumentUploadRequest request = @@ -160,12 +163,13 @@ public void testGet01() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testGet02() throws Exception { // given getConfigService().save(SITEID0, new DynamicObject(Map.of(MAX_DOCUMENT_SIZE_BYTES, "5"))); - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(SITEID0)) { GetDocumentUploadRequest request = new GetDocumentUploadRequest().siteId(SITEID0); @@ -184,7 +188,8 @@ public void testGet02() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testGet03() throws Exception { // given final int contentLength = 100; @@ -193,7 +198,7 @@ public void testGet03() throws Exception { GetDocumentUploadRequest request = new GetDocumentUploadRequest().siteId(SITEID0).contentLength(contentLength); - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(SITEID0)) { // when HttpResponse response = client.getDocumentUploadAsHttpResponse(request); @@ -210,7 +215,8 @@ public void testGet03() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testGet04() throws Exception { // given final int contentLength = 5; @@ -220,7 +226,7 @@ public void testGet04() throws Exception { GetDocumentUploadRequest request = new GetDocumentUploadRequest().siteId(SITEID0).contentLength(contentLength); - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(SITEID0)) { // when HttpResponse response = client.getDocumentUploadAsHttpResponse(request); @@ -236,7 +242,8 @@ public void testGet04() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testGet05() throws Exception { // given getConfigService().save(SITEID1, new DynamicObject(Map.of(MAX_DOCUMENTS, "1"))); @@ -245,10 +252,10 @@ public void testGet05() throws Exception { new GetDocumentUploadRequest().siteId(SITEID1).contentLength(1); HttpResponse response = - getFormKiqClients().get(0).getDocumentUploadAsHttpResponse(request); + getFormKiqClients(SITEID1).get(0).getDocumentUploadAsHttpResponse(request); assertEquals(STATUS_OK, response.statusCode()); - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(SITEID1)) { // when response = client.getDocumentUploadAsHttpResponse(request); @@ -265,9 +272,10 @@ public void testGet05() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testOptions01() throws Exception { - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { // given // when HttpResponse response = client.optionsDocumentUpload(); @@ -282,9 +290,10 @@ public void testOptions01() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testPost01() throws Exception { - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { // given DocumentTag tag0 = new DocumentTag().key("test").value("this"); AddLargeDocumentRequest request = diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/awstest/PrivateWebhooksRequestTest.java b/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/PrivateWebhooksRequestTest.java similarity index 88% rename from lambda-api/src/test/java/com/formkiq/stacks/api/awstest/PrivateWebhooksRequestTest.java rename to lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/PrivateWebhooksRequestTest.java index 6c8257eef..84a14752f 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/awstest/PrivateWebhooksRequestTest.java +++ b/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/PrivateWebhooksRequestTest.java @@ -23,18 +23,20 @@ */ package com.formkiq.stacks.api.awstest; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.net.http.HttpHeaders; import java.net.http.HttpResponse; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.concurrent.TimeUnit; import java.util.function.BiPredicate; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; import com.formkiq.stacks.client.FormKiqClientV1; import com.formkiq.stacks.client.HttpService; import com.formkiq.stacks.client.HttpServiceJava; @@ -70,9 +72,10 @@ public class PrivateWebhooksRequestTest extends AbstractApiTest { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testOptions01() throws Exception { - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { assertEquals(STATUS_NO_CONTENT, client.optionsWebhooks().statusCode()); assertEquals(STATUS_NO_CONTENT, client.optionsWebhooks(new OptionsWebhookRequest().webhookId("1")).statusCode()); @@ -84,9 +87,10 @@ public void testOptions01() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testPublicWebhooks01() throws Exception { - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { // given String id = client.addWebhook(new AddWebhookRequest().name("paypal").enabled("private")).id(); String urlpath = getRootHttpUrl() + "/private/webhooks/" + id; @@ -134,9 +138,10 @@ public boolean test(final String t, final String u) { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testPublicWebhooks02() throws Exception { - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { // given String id = client.addWebhook(new AddWebhookRequest().name("paypal").enabled("private")).id(); String urlpath = getRootHttpUrl() + "/private/webhooks/" + id; diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/awstest/PublicWebhooksRequestTest.java b/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/PublicWebhooksRequestTest.java similarity index 89% rename from lambda-api/src/test/java/com/formkiq/stacks/api/awstest/PublicWebhooksRequestTest.java rename to lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/PublicWebhooksRequestTest.java index ef779a8ab..38094669c 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/awstest/PublicWebhooksRequestTest.java +++ b/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/PublicWebhooksRequestTest.java @@ -23,18 +23,20 @@ */ package com.formkiq.stacks.api.awstest; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.net.http.HttpHeaders; import java.net.http.HttpResponse; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.concurrent.TimeUnit; import java.util.function.BiPredicate; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; import com.formkiq.stacks.client.FormKiqClientV1; import com.formkiq.stacks.client.HttpService; import com.formkiq.stacks.client.HttpServiceJava; @@ -74,9 +76,10 @@ public class PublicWebhooksRequestTest extends AbstractApiTest { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testOptions01() throws Exception { - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { assertEquals(STATUS_NO_CONTENT, client.optionsWebhooks().statusCode()); assertEquals(STATUS_NO_CONTENT, client.optionsWebhooks(new OptionsWebhookRequest().webhookId("1")).statusCode()); @@ -88,9 +91,10 @@ public void testOptions01() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testPublicWebhooks01() throws Exception { - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { // given String id = client.addWebhook(new AddWebhookRequest().name("paypal")).id(); String urlpath = getRootHttpUrl() + "/public/webhooks/" + id; @@ -136,7 +140,8 @@ public boolean test(final String t, final String u) { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testPublicWebhooks02() throws Exception { // given String urlpath = getRootHttpUrl() + "/public/webhooks"; @@ -166,7 +171,8 @@ public boolean test(final String t, final String u) { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testPublicWebhooks03() throws Exception { // given String urlpath = getRootHttpUrl() + "/public/webhooks/asdffgdfg"; @@ -196,9 +202,10 @@ public boolean test(final String t, final String u) { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testPublicWebhooks04() throws Exception { - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { // given String id = client.addWebhook(new AddWebhookRequest().name("paypal").enabled("private")).id(); String urlpath = getRootHttpUrl() + "/public/webhooks/" + id; diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/awstest/SitesRequestTest.java b/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/SitesRequestTest.java similarity index 86% rename from lambda-api/src/test/java/com/formkiq/stacks/api/awstest/SitesRequestTest.java rename to lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/SitesRequestTest.java index 2bb007567..9035429ad 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/awstest/SitesRequestTest.java +++ b/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/SitesRequestTest.java @@ -23,11 +23,13 @@ */ package com.formkiq.stacks.api.awstest; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.net.http.HttpResponse; -import org.junit.Test; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; import com.formkiq.stacks.client.FormKiqClientV1; import com.formkiq.stacks.client.models.Sites; import software.amazon.awssdk.services.cognitoidentityprovider.model.AuthenticationResultType; @@ -51,7 +53,8 @@ public class SitesRequestTest extends AbstractApiTest { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testSites01() throws Exception { // given AuthenticationResultType token = login(USER_EMAIL, USER_PASSWORD); @@ -72,9 +75,9 @@ public void testSites01() throws Exception { * * @throws Exception Exception */ - @Test // (timeout = TEST_TIMEOUT) + @Test public void testOptions01() throws Exception { - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { HttpResponse response = client.optionsSites(); assertEquals(HTTP_STATUS_NO_CONTENT, response.statusCode()); assertPreflightedCorsHeaders(response.headers()); diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/awstest/VersionRequestTest.java b/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/VersionRequestTest.java similarity index 81% rename from lambda-api/src/test/java/com/formkiq/stacks/api/awstest/VersionRequestTest.java rename to lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/VersionRequestTest.java index 3cb7bf0ed..b4504c8f5 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/awstest/VersionRequestTest.java +++ b/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/VersionRequestTest.java @@ -23,10 +23,12 @@ */ package com.formkiq.stacks.api.awstest; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import java.net.http.HttpResponse; -import org.junit.Test; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; import com.formkiq.stacks.client.FormKiqClientV1; /** @@ -48,10 +50,11 @@ public class VersionRequestTest extends AbstractApiTest { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testVersion01() throws Exception { // given - for (FormKiqClientV1 c : getFormKiqClients()) { + for (FormKiqClientV1 c : getFormKiqClients(null)) { assertNotNull(c.getVersion().version()); assertNotNull(c.getVersion().type()); } @@ -62,9 +65,10 @@ public void testVersion01() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testOptions01() throws Exception { - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { HttpResponse response = client.optionsVersion(); assertEquals(HTTP_STATUS_NO_CONTENT, response.statusCode()); assertPreflightedCorsHeaders(response.headers()); diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/awstest/WebhookTagsRequestTest.java b/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/WebhookTagsRequestTest.java similarity index 85% rename from lambda-api/src/test/java/com/formkiq/stacks/api/awstest/WebhookTagsRequestTest.java rename to lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/WebhookTagsRequestTest.java index 930a1699e..9cf6763b5 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/awstest/WebhookTagsRequestTest.java +++ b/lambda-api/src/integration/java/com/formkiq/stacks/api/awstest/WebhookTagsRequestTest.java @@ -23,11 +23,13 @@ */ package com.formkiq.stacks.api.awstest; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.UUID; -import org.junit.Test; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; import com.formkiq.stacks.client.FormKiqClientV1; import com.formkiq.stacks.client.models.WebhookTag; import com.formkiq.stacks.client.models.WebhookTags; @@ -55,9 +57,10 @@ public class WebhookTagsRequestTest extends AbstractApiTest { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testOptions01() throws Exception { - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { OptionsWebhookTagsRequest req = new OptionsWebhookTagsRequest().webhookId(UUID.randomUUID().toString()); assertEquals(STATUS_NO_CONTENT, client.optionsWebhookTags(req).statusCode()); @@ -69,9 +72,10 @@ public void testOptions01() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testPublicWebhooks01() throws Exception { - for (FormKiqClientV1 client : getFormKiqClients()) { + for (FormKiqClientV1 client : getFormKiqClients(null)) { // given String id = client.addWebhook(new AddWebhookRequest().name("paypal")).id(); diff --git a/lambda-api/src/main/java/com/formkiq/stacks/api/AbstractCoreRequestHandler.java b/lambda-api/src/main/java/com/formkiq/stacks/api/AbstractCoreRequestHandler.java index 0701bf528..ef3699c8d 100644 --- a/lambda-api/src/main/java/com/formkiq/stacks/api/AbstractCoreRequestHandler.java +++ b/lambda-api/src/main/java/com/formkiq/stacks/api/AbstractCoreRequestHandler.java @@ -38,8 +38,6 @@ import com.formkiq.aws.services.lambda.LambdaInputRecord; import com.formkiq.aws.services.lambda.exceptions.NotFoundException; import com.formkiq.aws.services.lambda.services.CacheService; -import com.formkiq.aws.services.lambda.services.ConfigService; -import com.formkiq.aws.services.lambda.services.ConfigServiceExtension; import com.formkiq.aws.services.lambda.services.DynamoDbCacheServiceExtension; import com.formkiq.aws.sns.SnsConnectionBuilder; import com.formkiq.aws.sqs.SqsConnectionBuilder; @@ -54,8 +52,12 @@ import com.formkiq.module.actions.services.ActionsServiceExtension; import com.formkiq.module.lambdaservices.AwsServiceCache; import com.formkiq.module.lambdaservices.ClassServiceExtension; +import com.formkiq.module.ocr.DocumentOcrService; +import com.formkiq.module.ocr.DocumentOcrServiceExtension; import com.formkiq.plugins.tagschema.DocumentTagSchemaPlugin; import com.formkiq.plugins.tagschema.DocumentTagSchemaPluginExtension; +import com.formkiq.stacks.api.handler.ConfigurationRequestHandler; +import com.formkiq.stacks.api.handler.ConfigurationApiKeysRequestHandler; import com.formkiq.stacks.api.handler.DocumentIdContentRequestHandler; import com.formkiq.stacks.api.handler.DocumentIdRequestHandler; import com.formkiq.stacks.api.handler.DocumentIdUrlRequestHandler; @@ -95,6 +97,10 @@ import com.formkiq.stacks.api.handler.WebhooksIdRequestHandler; import com.formkiq.stacks.api.handler.WebhooksRequestHandler; import com.formkiq.stacks.api.handler.WebhooksTagsRequestHandler; +import com.formkiq.stacks.dynamodb.ApiKeysService; +import com.formkiq.stacks.dynamodb.ApiKeysServiceExtension; +import com.formkiq.stacks.dynamodb.ConfigService; +import com.formkiq.stacks.dynamodb.ConfigServiceExtension; import com.formkiq.stacks.dynamodb.DocumentCountService; import com.formkiq.stacks.dynamodb.DocumentCountServiceExtension; import com.formkiq.stacks.dynamodb.DocumentSearchService; @@ -141,6 +147,8 @@ public static void buildUrlMap() { URL_MAP.put("options", new DocumentsOptionsRequestHandler()); addRequestHandler(new VersionRequestHandler()); addRequestHandler(new SitesRequestHandler()); + addRequestHandler(new ConfigurationRequestHandler()); + addRequestHandler(new ConfigurationApiKeysRequestHandler()); addRequestHandler(new DocumentVersionsRequestHandler()); addRequestHandler(new DocumentVersionsKeyRequestHandler()); addRequestHandler(new DocumentTagsRequestHandler()); @@ -189,18 +197,20 @@ public static void configureHandler(final Map map, final Region final AwsCredentialsProvider credentialsProvider, final Map awsServiceEndpoints, final DocumentTagSchemaPlugin schemaEvents) { - DynamoDbConnectionBuilder db = - new DynamoDbConnectionBuilder().setRegion(region).setCredentials(credentialsProvider) - .setEndpointOverride(awsServiceEndpoints.get("dynamodb")); + final boolean enableAwsXray = "true".equals(map.get("ENABLE_AWS_X_RAY")); + + DynamoDbConnectionBuilder db = new DynamoDbConnectionBuilder(enableAwsXray).setRegion(region) + .setCredentials(credentialsProvider) + .setEndpointOverride(awsServiceEndpoints.get("dynamodb")); AwsServiceCache.register(DynamoDbConnectionBuilder.class, new DynamoDbConnectionBuilderExtension(db)); - SsmConnectionBuilder ssm = new SsmConnectionBuilder().setRegion(region) + SsmConnectionBuilder ssm = new SsmConnectionBuilder(enableAwsXray).setRegion(region) .setCredentials(credentialsProvider).setEndpointOverride(awsServiceEndpoints.get("ssm")); AwsServiceCache.register(SsmConnectionBuilder.class, new ClassServiceExtension(ssm)); - SqsConnectionBuilder sqs = new SqsConnectionBuilder().setRegion(region) + SqsConnectionBuilder sqs = new SqsConnectionBuilder(enableAwsXray).setRegion(region) .setCredentials(credentialsProvider).setEndpointOverride(awsServiceEndpoints.get("sqs")); AwsServiceCache.register(SqsConnectionBuilder.class, new ClassServiceExtension(sqs)); @@ -212,13 +222,13 @@ public static void configureHandler(final Map map, final Region new ClassServiceExtension<>(credentialsProvider.resolveCredentials())); } - SnsConnectionBuilder sns = new SnsConnectionBuilder().setRegion(region) + SnsConnectionBuilder sns = new SnsConnectionBuilder(enableAwsXray).setRegion(region) .setCredentials(credentialsProvider).setEndpointOverride(awsServiceEndpoints.get("sns")); AwsServiceCache.register(ActionsNotificationService.class, new ActionsNotificationServiceExtension(sns)); - S3ConnectionBuilder s3 = new S3ConnectionBuilder().setRegion(region) + S3ConnectionBuilder s3 = new S3ConnectionBuilder(enableAwsXray).setRegion(region) .setCredentials(credentialsProvider).setEndpointOverride(awsServiceEndpoints.get("s3")); AwsServiceCache.register(ActionsService.class, new ActionsServiceExtension()); @@ -233,7 +243,9 @@ public static void configureHandler(final Map map, final Region AwsServiceCache.register(DocumentCountService.class, new DocumentCountServiceExtension()); AwsServiceCache.register(FolderIndexProcessor.class, new IndexProcessorExtension()); AwsServiceCache.register(ConfigService.class, new ConfigServiceExtension()); + AwsServiceCache.register(ApiKeysService.class, new ApiKeysServiceExtension()); AwsServiceCache.register(DocumentSyncService.class, new DocumentSyncServiceExtension()); + AwsServiceCache.register(DocumentOcrService.class, new DocumentOcrServiceExtension()); awsServices = new CoreAwsServiceCache().environment(map).debug("true".equals(map.get("DEBUG"))); diff --git a/lambda-api/src/main/java/com/formkiq/stacks/api/handler/ConfigurationApiKeysRequestHandler.java b/lambda-api/src/main/java/com/formkiq/stacks/api/handler/ConfigurationApiKeysRequestHandler.java new file mode 100644 index 000000000..3b819d92e --- /dev/null +++ b/lambda-api/src/main/java/com/formkiq/stacks/api/handler/ConfigurationApiKeysRequestHandler.java @@ -0,0 +1,133 @@ +/** + * MIT License + * + * Copyright (c) 2018 - 2020 FormKiQ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.formkiq.stacks.api.handler; + +import static com.formkiq.aws.services.lambda.ApiGatewayRequestEventUtil.getCallingCognitoUsername; +import static com.formkiq.aws.services.lambda.ApiResponseStatus.SC_OK; +import java.util.List; +import java.util.Map; +import com.amazonaws.services.lambda.runtime.LambdaLogger; +import com.formkiq.aws.dynamodb.DynamicObject; +import com.formkiq.aws.services.lambda.ApiAuthorizer; +import com.formkiq.aws.services.lambda.ApiGatewayRequestEvent; +import com.formkiq.aws.services.lambda.ApiGatewayRequestEventUtil; +import com.formkiq.aws.services.lambda.ApiGatewayRequestHandler; +import com.formkiq.aws.services.lambda.ApiMapResponse; +import com.formkiq.aws.services.lambda.ApiRequestHandlerResponse; +import com.formkiq.aws.services.lambda.exceptions.BadException; +import com.formkiq.aws.services.lambda.exceptions.UnauthorizedException; +import com.formkiq.module.lambdaservices.AwsServiceCache; +import com.formkiq.stacks.dynamodb.ApiKeysService; + +/** {@link ApiGatewayRequestHandler} for "/configuration/apiKeys". */ +public class ConfigurationApiKeysRequestHandler + implements ApiGatewayRequestHandler, ApiGatewayRequestEventUtil { + + /** + * constructor. + * + */ + public ConfigurationApiKeysRequestHandler() {} + + @Override + public void beforeDelete(final LambdaLogger logger, final ApiGatewayRequestEvent event, + final ApiAuthorizer authorizer, final AwsServiceCache awsServices) throws Exception { + checkPermissions(authorizer); + } + + @Override + public void beforeGet(final LambdaLogger logger, final ApiGatewayRequestEvent event, + final ApiAuthorizer authorizer, final AwsServiceCache awsServices) throws Exception { + checkPermissions(authorizer); + } + + @Override + public void beforePost(final LambdaLogger logger, final ApiGatewayRequestEvent event, + final ApiAuthorizer authorizer, final AwsServiceCache awsServices) throws Exception { + checkPermissions(authorizer); + } + + private void checkPermissions(final ApiAuthorizer authorizer) throws UnauthorizedException { + if (!authorizer.isUserAdmin() && !authorizer.isCallerIamUser() + && !authorizer.isCallerAssumeRole()) { + throw new UnauthorizedException("user is unauthorized"); + } + } + + @Override + public ApiRequestHandlerResponse delete(final LambdaLogger logger, + final ApiGatewayRequestEvent event, final ApiAuthorizer authorizer, + final AwsServiceCache awsservice) throws Exception { + + String apiKey = event.getQueryStringParameter("apiKey"); + + ApiKeysService apiKeysService = awsservice.getExtension(ApiKeysService.class); + apiKeysService.deleteApiKey(apiKey); + + return new ApiRequestHandlerResponse(SC_OK, + new ApiMapResponse(Map.of("message", "ApiKey deleted"))); + } + + @Override + public ApiRequestHandlerResponse get(final LambdaLogger logger, + final ApiGatewayRequestEvent event, final ApiAuthorizer authorizer, + final AwsServiceCache awsservice) throws Exception { + + ApiKeysService apiKeysService = awsservice.getExtension(ApiKeysService.class); + + List list = apiKeysService.list(); + + Map map = Map.of("apiKeys", list); + return new ApiRequestHandlerResponse(SC_OK, new ApiMapResponse(map)); + } + + @Override + public String getRequestUrl() { + return "/configuration/apiKeys"; + } + + @SuppressWarnings("unchecked") + @Override + public ApiRequestHandlerResponse post(final LambdaLogger logger, + final ApiGatewayRequestEvent event, final ApiAuthorizer authorizer, + final AwsServiceCache awsservice) throws Exception { + + String siteId = authorizer.getSiteId(); + + ApiKeysService apiKeysService = awsservice.getExtension(ApiKeysService.class); + Map body = fromBodyToObject(logger, event, Map.class); + String name = body.get("name"); + + if (name != null) { + + String userId = getCallingCognitoUsername(event); + String apiKey = apiKeysService.createApiKey(siteId, name, userId); + + return new ApiRequestHandlerResponse(SC_OK, + new ApiMapResponse(Map.of("name", name, "apiKey", apiKey))); + } + + throw new BadException("missing required body parameters"); + } +} diff --git a/lambda-api/src/main/java/com/formkiq/stacks/api/handler/ConfigurationRequestHandler.java b/lambda-api/src/main/java/com/formkiq/stacks/api/handler/ConfigurationRequestHandler.java new file mode 100644 index 000000000..7aca34aed --- /dev/null +++ b/lambda-api/src/main/java/com/formkiq/stacks/api/handler/ConfigurationRequestHandler.java @@ -0,0 +1,130 @@ +/** + * MIT License + * + * Copyright (c) 2018 - 2020 FormKiQ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.formkiq.stacks.api.handler; + +import static com.formkiq.aws.services.lambda.ApiResponseStatus.SC_OK; +import static com.formkiq.stacks.dynamodb.ConfigService.CHATGPT_API_KEY; +import static com.formkiq.stacks.dynamodb.ConfigService.MAX_DOCUMENTS; +import static com.formkiq.stacks.dynamodb.ConfigService.MAX_DOCUMENT_SIZE_BYTES; +import static com.formkiq.stacks.dynamodb.ConfigService.MAX_WEBHOOKS; +import java.util.HashMap; +import java.util.Map; +import com.amazonaws.services.lambda.runtime.LambdaLogger; +import com.formkiq.aws.dynamodb.DynamicObject; +import com.formkiq.aws.services.lambda.ApiAuthorizer; +import com.formkiq.aws.services.lambda.ApiGatewayRequestEvent; +import com.formkiq.aws.services.lambda.ApiGatewayRequestEventUtil; +import com.formkiq.aws.services.lambda.ApiGatewayRequestHandler; +import com.formkiq.aws.services.lambda.ApiMapResponse; +import com.formkiq.aws.services.lambda.ApiRequestHandlerResponse; +import com.formkiq.aws.services.lambda.exceptions.BadException; +import com.formkiq.aws.services.lambda.exceptions.UnauthorizedException; +import com.formkiq.module.lambdaservices.AwsServiceCache; +import com.formkiq.stacks.dynamodb.ConfigService; + +/** {@link ApiGatewayRequestHandler} for "/configuration". */ +public class ConfigurationRequestHandler + implements ApiGatewayRequestHandler, ApiGatewayRequestEventUtil { + + /** + * constructor. + * + */ + public ConfigurationRequestHandler() {} + + @Override + public void beforeGet(final LambdaLogger logger, final ApiGatewayRequestEvent event, + final ApiAuthorizer authorizer, final AwsServiceCache awsServices) throws Exception { + checkPermissions(authorizer); + } + + @Override + public void beforePatch(final LambdaLogger logger, final ApiGatewayRequestEvent event, + final ApiAuthorizer authorizer, final AwsServiceCache awsServices) throws Exception { + checkPermissions(authorizer); + } + + private void checkPermissions(final ApiAuthorizer authorizer) throws UnauthorizedException { + if (!authorizer.isUserAdmin() && !authorizer.isCallerIamUser()) { + throw new UnauthorizedException("user is unauthorized"); + } + } + + @Override + public ApiRequestHandlerResponse get(final LambdaLogger logger, + final ApiGatewayRequestEvent event, final ApiAuthorizer authorizer, + final AwsServiceCache awsservice) throws Exception { + + String siteId = authorizer.getSiteId(); + ConfigService configService = awsservice.getExtension(ConfigService.class); + + DynamicObject obj = configService.get(siteId); + Map map = new HashMap<>(); + map.put("chatGptApiKey", obj.getOrDefault(CHATGPT_API_KEY, "")); + map.put("maxContentLengthBytes", obj.getOrDefault(MAX_DOCUMENT_SIZE_BYTES, "")); + map.put("maxDocuments", obj.getOrDefault(MAX_DOCUMENTS, "")); + map.put("maxWebhooks", obj.getOrDefault(MAX_WEBHOOKS, "")); + + return new ApiRequestHandlerResponse(SC_OK, new ApiMapResponse(map)); + } + + @Override + public String getRequestUrl() { + return "/configuration"; + } + + @SuppressWarnings("unchecked") + @Override + public ApiRequestHandlerResponse patch(final LambdaLogger logger, + final ApiGatewayRequestEvent event, final ApiAuthorizer authorizer, + final AwsServiceCache awsservice) throws Exception { + + String siteId = authorizer.getSiteId(); + + Map body = fromBodyToObject(logger, event, Map.class); + + Map map = new HashMap<>(); + put(map, body, CHATGPT_API_KEY, "chatGptApiKey"); + put(map, body, MAX_DOCUMENT_SIZE_BYTES, "maxContentLengthBytes"); + put(map, body, MAX_DOCUMENTS, "maxDocuments"); + put(map, body, MAX_WEBHOOKS, "maxWebhooks"); + + if (!map.isEmpty()) { + ConfigService configService = awsservice.getExtension(ConfigService.class); + configService.save(siteId, new DynamicObject(map)); + + return new ApiRequestHandlerResponse(SC_OK, + new ApiMapResponse(Map.of("message", "Config saved"))); + } + + throw new BadException("missing required body parameters"); + } + + private void put(final Map map, final Map body, + final String mapKey, final String bodyKey) { + if (body.containsKey(bodyKey)) { + map.put(mapKey, body.get(bodyKey)); + } + } +} diff --git a/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentIdContentRequestHandler.java b/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentIdContentRequestHandler.java index 31ee67ccf..15b78193e 100644 --- a/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentIdContentRequestHandler.java +++ b/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentIdContentRequestHandler.java @@ -31,6 +31,7 @@ import com.amazonaws.services.lambda.runtime.LambdaLogger; import com.formkiq.aws.dynamodb.DynamoDbConnectionBuilder; import com.formkiq.aws.dynamodb.model.DocumentItem; +import com.formkiq.aws.dynamodb.objects.MimeType; import com.formkiq.aws.s3.PresignGetUrlConfig; import com.formkiq.aws.s3.S3Service; import com.formkiq.aws.services.lambda.ApiAuthorizer; @@ -42,7 +43,6 @@ import com.formkiq.aws.services.lambda.ApiResponse; import com.formkiq.aws.services.lambda.exceptions.NotFoundException; import com.formkiq.module.lambdaservices.AwsServiceCache; -import com.formkiq.stacks.common.formats.MimeType; import com.formkiq.stacks.dynamodb.DocumentService; import com.formkiq.stacks.dynamodb.DocumentVersionService; diff --git a/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentIdRequestHandler.java b/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentIdRequestHandler.java index 38119e7c8..081485a35 100644 --- a/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentIdRequestHandler.java +++ b/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentIdRequestHandler.java @@ -23,6 +23,7 @@ */ package com.formkiq.stacks.api.handler; +import static com.formkiq.aws.services.lambda.ApiGatewayRequestEventUtil.getCallingCognitoUsername; import static com.formkiq.aws.dynamodb.SiteIdKeyGenerator.DEFAULT_SITE_ID; import static com.formkiq.aws.dynamodb.SiteIdKeyGenerator.createDatabaseKey; import static com.formkiq.aws.dynamodb.SiteIdKeyGenerator.createS3Key; @@ -48,6 +49,7 @@ import com.formkiq.aws.dynamodb.model.DocumentMetadata; import com.formkiq.aws.dynamodb.model.DocumentTag; import com.formkiq.aws.dynamodb.model.DynamicDocumentItem; +import com.formkiq.aws.dynamodb.objects.DateUtil; import com.formkiq.aws.s3.S3ObjectMetadata; import com.formkiq.aws.s3.S3Service; import com.formkiq.aws.services.lambda.ApiAuthorizer; @@ -65,7 +67,6 @@ import com.formkiq.aws.services.lambda.services.CacheService; import com.formkiq.module.lambdaservices.AwsServiceCache; import com.formkiq.plugins.tagschema.DocumentTagSchemaPlugin; -import com.formkiq.stacks.dynamodb.DateUtil; import com.formkiq.stacks.dynamodb.DocumentCountService; import com.formkiq.stacks.dynamodb.DocumentItemToDynamicDocumentItem; import com.formkiq.stacks.dynamodb.DocumentService; diff --git a/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentTagRequestHandler.java b/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentTagRequestHandler.java index f50dbc4e6..91862dc11 100644 --- a/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentTagRequestHandler.java +++ b/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentTagRequestHandler.java @@ -23,6 +23,7 @@ */ package com.formkiq.stacks.api.handler; +import static com.formkiq.aws.services.lambda.ApiGatewayRequestEventUtil.getCallingCognitoUsername; import static com.formkiq.aws.services.lambda.ApiResponseStatus.SC_OK; import java.util.ArrayList; import java.util.Arrays; diff --git a/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentTagsRequestHandler.java b/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentTagsRequestHandler.java index a9e73bf8c..a65156de2 100644 --- a/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentTagsRequestHandler.java +++ b/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentTagsRequestHandler.java @@ -23,6 +23,7 @@ */ package com.formkiq.stacks.api.handler; +import static com.formkiq.aws.services.lambda.ApiGatewayRequestEventUtil.getCallingCognitoUsername; import static com.formkiq.aws.services.lambda.ApiResponseStatus.SC_CREATED; import static com.formkiq.aws.services.lambda.ApiResponseStatus.SC_OK; import java.util.ArrayList; diff --git a/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentsActionsRequestHandler.java b/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentsActionsRequestHandler.java index 89c0547af..c070d05dc 100644 --- a/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentsActionsRequestHandler.java +++ b/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentsActionsRequestHandler.java @@ -23,6 +23,7 @@ */ package com.formkiq.stacks.api.handler; +import static com.formkiq.aws.services.lambda.ApiGatewayRequestEventUtil.getCallingCognitoUsername; import static com.formkiq.aws.services.lambda.ApiResponseStatus.SC_OK; import java.util.ArrayList; import java.util.Collection; diff --git a/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentsIdUploadRequestHandler.java b/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentsIdUploadRequestHandler.java index 85ce92024..304b491f9 100644 --- a/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentsIdUploadRequestHandler.java +++ b/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentsIdUploadRequestHandler.java @@ -23,6 +23,7 @@ */ package com.formkiq.stacks.api.handler; +import static com.formkiq.aws.services.lambda.ApiGatewayRequestEventUtil.getCallingCognitoUsername; import static com.formkiq.aws.services.lambda.ApiResponseStatus.SC_OK; import java.net.URL; import java.net.URLDecoder; diff --git a/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentsOcrRequestHandler.java b/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentsOcrRequestHandler.java index d7a600d86..5ab7b86f7 100644 --- a/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentsOcrRequestHandler.java +++ b/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentsOcrRequestHandler.java @@ -23,10 +23,39 @@ */ package com.formkiq.stacks.api.handler; +import static com.formkiq.aws.services.lambda.ApiGatewayRequestEventUtil.getCallingCognitoUsername; +import static com.formkiq.aws.services.lambda.ApiResponseStatus.SC_NOT_FOUND; +import static com.formkiq.aws.services.lambda.ApiResponseStatus.SC_OK; +import static com.formkiq.module.ocr.DocumentOcrService.PREFIX_TEMP_FILES; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import com.amazonaws.services.lambda.runtime.LambdaLogger; +import com.formkiq.aws.dynamodb.DynamicObject; +import com.formkiq.aws.s3.PresignGetUrlConfig; +import com.formkiq.aws.s3.S3Service; +import com.formkiq.aws.services.lambda.ApiAuthorizer; +import com.formkiq.aws.services.lambda.ApiGatewayRequestEvent; +import com.formkiq.aws.services.lambda.ApiGatewayRequestEventUtil; import com.formkiq.aws.services.lambda.ApiGatewayRequestHandler; +import com.formkiq.aws.services.lambda.ApiMapResponse; +import com.formkiq.aws.services.lambda.ApiRequestHandlerResponse; +import com.formkiq.aws.services.lambda.ApiResponseStatus; +import com.formkiq.aws.services.lambda.exceptions.BadException; +import com.formkiq.aws.services.lambda.exceptions.NotFoundException; +import com.formkiq.module.lambdaservices.AwsServiceCache; +import com.formkiq.module.ocr.DocumentOcrService; +import com.formkiq.module.ocr.OcrRequest; +import com.formkiq.module.ocr.OcrScanStatus; +import com.formkiq.stacks.dynamodb.DocumentService; /** {@link ApiGatewayRequestHandler} for "/documents/{documentId}/ocr". */ -public class DocumentsOcrRequestHandler extends AbstractPaymentRequiredRequestHandler { +public class DocumentsOcrRequestHandler + implements ApiGatewayRequestHandler, ApiGatewayRequestEventUtil { /** {@link DocumentsOcrRequestHandler} URL. */ public static final String URL = "/documents/{documentId}/ocr"; @@ -37,8 +66,219 @@ public class DocumentsOcrRequestHandler extends AbstractPaymentRequiredRequestHa */ public DocumentsOcrRequestHandler() {} + /** + * Build Get Response. + * + * @param obj {@link DynamicObject} + * @param documentId {@link String} + * @return {@link Map} + */ + private Map buildGetResponse(final DynamicObject obj, final String documentId) { + Map map = new HashMap<>(); + map.put("documentId", documentId); + + if (obj != null) { + + map.put("insertedDate", obj.get("insertedDate")); + map.put("contentType", obj.get("contentType")); + map.put("userId", obj.get("userId")); + map.put("ocrEngine", obj.get("ocrEngine")); + map.put("ocrStatus", obj.get("ocrStatus")); + } + + return map; + } + + @Override + public ApiRequestHandlerResponse delete(final LambdaLogger logger, + final ApiGatewayRequestEvent event, final ApiAuthorizer authorizer, + final AwsServiceCache awsservice) throws Exception { + + ApiMapResponse resp = new ApiMapResponse(); + String siteId = authorizer.getSiteId(); + String documentId = event.getPathParameters().get("documentId"); + + DocumentOcrService ocrService = awsservice.getExtension(DocumentOcrService.class); + ocrService.delete(siteId, documentId); + + return new ApiRequestHandlerResponse(SC_OK, resp); + } + + @Override + public ApiRequestHandlerResponse get(final LambdaLogger logger, + final ApiGatewayRequestEvent event, final ApiAuthorizer authorizer, + final AwsServiceCache awsservice) throws Exception { + + ApiResponseStatus status = SC_OK; + String siteId = authorizer.getSiteId(); + String documentId = event.getPathParameters().get("documentId"); + + final boolean contentUrl = event.getQueryStringParameters() != null + && event.getQueryStringParameters().containsKey("contentUrl"); + + final boolean textOnly = event.getQueryStringParameters() != null + && event.getQueryStringParameters().containsKey("text"); + + DocumentOcrService ocrService = awsservice.getExtension(DocumentOcrService.class); + + DynamicObject obj = ocrService.get(siteId, documentId); + + Map map = buildGetResponse(obj, documentId); + + if (map.containsKey("ocrStatus")) { + + if (map.get("ocrStatus").equals(OcrScanStatus.SUCCESSFUL.name().toLowerCase())) { + + S3Service s3 = awsservice.getExtension(S3Service.class); + + String jobId = obj.getString("jobId"); + + List s3Keys = ocrService.getOcrS3Keys(siteId, documentId, jobId); + if (s3Keys.isEmpty()) { + throw new NotFoundException("OCR results not found"); + } + + if (contentUrl) { + + List contentUrls = getContentUrls(awsservice, ocrService, s3, s3Keys, textOnly); + map.put("contentUrls", contentUrls); + + } else { + + String content = getS3Content(awsservice, ocrService, s3, s3Keys, textOnly); + map.put("data", content); + } + + } + + } else { + status = SC_NOT_FOUND; + } + + ApiMapResponse resp = new ApiMapResponse(map); + return new ApiRequestHandlerResponse(status, resp); + } + + /** + * Get S3 Content Urls. + * + * @param awsservice {@link AwsServiceCache} + * @param ocrService {@link DocumentOcrService} + * @param s3 {@link S3Service} + * @param s3Keys {@link List} {@link String} + * @param textOnly boolean + * @return {@link List} {@link String} + */ + private List getContentUrls(final AwsServiceCache awsservice, + final DocumentOcrService ocrService, final S3Service s3, final List s3Keys, + final boolean textOnly) { + + String ocrBucket = awsservice.environment("OCR_S3_BUCKET"); + List newS3Keys = new ArrayList<>(); + + if (textOnly) { + + s3Keys.forEach(s3Key -> { + String content = s3.getContentAsString(ocrBucket, s3Key, null); + content = ocrService.toText(content); + + String newKey = PREFIX_TEMP_FILES + s3Key; + s3.putObject(ocrBucket, newKey, content.getBytes(StandardCharsets.UTF_8), "text/plain"); + + newS3Keys.add(newKey); + }); + + } else { + newS3Keys.addAll(s3Keys); + } + + PresignGetUrlConfig config = new PresignGetUrlConfig(); + List contentUrls = newS3Keys.stream().map( + s3key -> s3.presignGetUrl(ocrBucket, s3key, Duration.ofHours(1), null, config).toString()) + .collect(Collectors.toList()); + return contentUrls; + } + @Override public String getRequestUrl() { return URL; } + + /** + * Get S3 Content. + * + * @param awsservice {@link AwsServiceCache} + * @param ocrService {@link DocumentOcrService} + * @param s3 {@link S3Service} + * @param s3Keys {@link List} {@link String} + * @param textOnly boolean + * @return {@link String} + */ + private String getS3Content(final AwsServiceCache awsservice, final DocumentOcrService ocrService, + final S3Service s3, final List s3Keys, final boolean textOnly) { + + String ocrBucket = awsservice.environment("OCR_S3_BUCKET"); + StringBuilder sb = new StringBuilder(); + + for (String s3Key : s3Keys) { + String content = s3.getContentAsString(ocrBucket, s3Key, null); + + if (textOnly) { + content = ocrService.toText(content); + } + + sb.append(content); + } + + return sb.toString(); + } + + @Override + public ApiRequestHandlerResponse post(final LambdaLogger logger, + final ApiGatewayRequestEvent event, final ApiAuthorizer authorizer, + final AwsServiceCache awsservice) throws Exception { + + ApiMapResponse resp = new ApiMapResponse(); + String siteId = authorizer.getSiteId(); + String documentId = event.getPathParameters().get("documentId"); + + DocumentService ds = awsservice.getExtension(DocumentService.class); + if (!ds.exists(siteId, documentId)) { + throw new NotFoundException("Document " + documentId + " not found."); + } + + OcrRequest request = fromBodyToObject(logger, event, OcrRequest.class); + String userId = getCallingCognitoUsername(event); + + DocumentOcrService ocrService = awsservice.getExtension(DocumentOcrService.class); + ocrService.convert(logger, awsservice, request, siteId, documentId, userId); + + return new ApiRequestHandlerResponse(SC_OK, resp); + } + + + @Override + public ApiRequestHandlerResponse put(final LambdaLogger logger, + final ApiGatewayRequestEvent event, final ApiAuthorizer authorizer, + final AwsServiceCache awsservice) throws Exception { + + ApiMapResponse resp = new ApiMapResponse(); + String siteId = authorizer.getSiteId(); + String documentId = event.getPathParameters().get("documentId"); + + String userId = getCallingCognitoUsername(event); + + Map map = fromBodyToMap(logger, event); + String contentType = (String) map.get("contentType"); + String content = (String) map.get("content"); + + if (contentType == null || content == null) { + throw new BadException("'content' and 'contentType' are required"); + } + + DocumentOcrService ocrService = awsservice.getExtension(DocumentOcrService.class); + ocrService.set(awsservice, siteId, documentId, userId, content, contentType); + + return new ApiRequestHandlerResponse(SC_OK, resp); + } } diff --git a/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentsRequestHandler.java b/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentsRequestHandler.java index b8d6be509..ee6f7f189 100644 --- a/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentsRequestHandler.java +++ b/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentsRequestHandler.java @@ -39,6 +39,7 @@ import com.formkiq.aws.dynamodb.PaginationResults; import com.formkiq.aws.dynamodb.model.DocumentItem; import com.formkiq.aws.dynamodb.model.DynamicDocumentItem; +import com.formkiq.aws.dynamodb.objects.DateUtil; import com.formkiq.aws.services.lambda.ApiAuthorizer; import com.formkiq.aws.services.lambda.ApiGatewayRequestEvent; import com.formkiq.aws.services.lambda.ApiGatewayRequestEventUtil; @@ -49,7 +50,6 @@ import com.formkiq.aws.services.lambda.exceptions.BadException; import com.formkiq.aws.services.lambda.services.CacheService; import com.formkiq.module.lambdaservices.AwsServiceCache; -import com.formkiq.stacks.dynamodb.DateUtil; import com.formkiq.stacks.dynamodb.DocumentItemToDynamicDocumentItem; import com.formkiq.stacks.dynamodb.DocumentService; diff --git a/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentsRestrictions.java b/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentsRestrictions.java index ca8fc1ba6..6b034bd2b 100644 --- a/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentsRestrictions.java +++ b/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentsRestrictions.java @@ -23,8 +23,8 @@ */ package com.formkiq.stacks.api.handler; -import com.formkiq.aws.services.lambda.services.ConfigService; import com.formkiq.module.lambdaservices.AwsServiceCache; +import com.formkiq.stacks.dynamodb.ConfigService; /** * Interface for providing restrictions around Document. diff --git a/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentsRestrictionsMaxContentLength.java b/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentsRestrictionsMaxContentLength.java index 0e4b93c06..0cdbd71ee 100644 --- a/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentsRestrictionsMaxContentLength.java +++ b/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentsRestrictionsMaxContentLength.java @@ -23,8 +23,8 @@ */ package com.formkiq.stacks.api.handler; -import com.formkiq.aws.services.lambda.services.ConfigService; import com.formkiq.module.lambdaservices.AwsServiceCache; +import com.formkiq.stacks.dynamodb.ConfigService; /** * {@link DocumentsRestrictions} for Max Number of Documents. diff --git a/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentsRestrictionsMaxDocuments.java b/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentsRestrictionsMaxDocuments.java index e066ac669..968d4a5b0 100644 --- a/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentsRestrictionsMaxDocuments.java +++ b/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentsRestrictionsMaxDocuments.java @@ -23,8 +23,8 @@ */ package com.formkiq.stacks.api.handler; -import com.formkiq.aws.services.lambda.services.ConfigService; import com.formkiq.module.lambdaservices.AwsServiceCache; +import com.formkiq.stacks.dynamodb.ConfigService; import com.formkiq.stacks.dynamodb.DocumentCountService; /** diff --git a/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentsUploadRequestHandler.java b/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentsUploadRequestHandler.java index f5f312a35..209a86e2f 100644 --- a/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentsUploadRequestHandler.java +++ b/lambda-api/src/main/java/com/formkiq/stacks/api/handler/DocumentsUploadRequestHandler.java @@ -23,6 +23,7 @@ */ package com.formkiq.stacks.api.handler; +import static com.formkiq.aws.services.lambda.ApiGatewayRequestEventUtil.getCallingCognitoUsername; import static com.formkiq.aws.services.lambda.ApiResponseStatus.SC_OK; import java.io.UnsupportedEncodingException; import java.net.URL; diff --git a/lambda-api/src/main/java/com/formkiq/stacks/api/handler/IndicesFolderMoveRequestHandler.java b/lambda-api/src/main/java/com/formkiq/stacks/api/handler/IndicesFolderMoveRequestHandler.java index 62fc4fe35..2653c01ca 100644 --- a/lambda-api/src/main/java/com/formkiq/stacks/api/handler/IndicesFolderMoveRequestHandler.java +++ b/lambda-api/src/main/java/com/formkiq/stacks/api/handler/IndicesFolderMoveRequestHandler.java @@ -23,6 +23,7 @@ */ package com.formkiq.stacks.api.handler; +import static com.formkiq.aws.services.lambda.ApiGatewayRequestEventUtil.getCallingCognitoUsername; import static com.formkiq.aws.services.lambda.ApiResponseStatus.SC_OK; import java.io.IOException; import java.util.ArrayList; diff --git a/lambda-api/src/main/java/com/formkiq/stacks/api/handler/PublicWebhooksRequestHandler.java b/lambda-api/src/main/java/com/formkiq/stacks/api/handler/PublicWebhooksRequestHandler.java index 49152b5b7..0b7200c58 100644 --- a/lambda-api/src/main/java/com/formkiq/stacks/api/handler/PublicWebhooksRequestHandler.java +++ b/lambda-api/src/main/java/com/formkiq/stacks/api/handler/PublicWebhooksRequestHandler.java @@ -26,7 +26,7 @@ import static com.formkiq.aws.dynamodb.SiteIdKeyGenerator.createDatabaseKey; import static com.formkiq.aws.services.lambda.ApiResponseStatus.MOVED_PERMANENTLY; import static com.formkiq.aws.services.lambda.ApiResponseStatus.SC_OK; -import static com.formkiq.aws.services.lambda.services.ConfigService.DOCUMENT_TIME_TO_LIVE; +import static com.formkiq.stacks.dynamodb.ConfigService.DOCUMENT_TIME_TO_LIVE; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; import java.time.Instant; @@ -51,9 +51,9 @@ import com.formkiq.aws.services.lambda.exceptions.TooManyRequestsException; import com.formkiq.aws.services.lambda.exceptions.UnauthorizedException; import com.formkiq.aws.services.lambda.services.CacheService; -import com.formkiq.aws.services.lambda.services.ConfigService; import com.formkiq.module.lambdaservices.AwsServiceCache; import com.formkiq.stacks.api.CoreAwsServiceCache; +import com.formkiq.stacks.dynamodb.ConfigService; import software.amazon.awssdk.utils.StringUtils; /** {@link ApiGatewayRequestHandler} for "/public/webhooks". */ diff --git a/lambda-api/src/main/java/com/formkiq/stacks/api/handler/SitesRequestHandler.java b/lambda-api/src/main/java/com/formkiq/stacks/api/handler/SitesRequestHandler.java index 6091b2f5b..b9853aab7 100644 --- a/lambda-api/src/main/java/com/formkiq/stacks/api/handler/SitesRequestHandler.java +++ b/lambda-api/src/main/java/com/formkiq/stacks/api/handler/SitesRequestHandler.java @@ -37,10 +37,10 @@ import com.formkiq.aws.services.lambda.ApiGatewayRequestHandler; import com.formkiq.aws.services.lambda.ApiMapResponse; import com.formkiq.aws.services.lambda.ApiRequestHandlerResponse; -import com.formkiq.aws.services.lambda.services.ConfigService; import com.formkiq.aws.ssm.SsmConnectionBuilder; import com.formkiq.aws.ssm.SsmService; import com.formkiq.module.lambdaservices.AwsServiceCache; +import com.formkiq.stacks.dynamodb.ConfigService; import software.amazon.awssdk.services.ssm.SsmClient; /** {@link ApiGatewayRequestHandler} for "/sites". */ diff --git a/lambda-api/src/main/java/com/formkiq/stacks/api/handler/WebhooksRequestHandler.java b/lambda-api/src/main/java/com/formkiq/stacks/api/handler/WebhooksRequestHandler.java index b9c3ddc2f..e2f77fd4c 100644 --- a/lambda-api/src/main/java/com/formkiq/stacks/api/handler/WebhooksRequestHandler.java +++ b/lambda-api/src/main/java/com/formkiq/stacks/api/handler/WebhooksRequestHandler.java @@ -24,9 +24,10 @@ package com.formkiq.stacks.api.handler; import static com.formkiq.aws.dynamodb.SiteIdKeyGenerator.DEFAULT_SITE_ID; +import static com.formkiq.aws.services.lambda.ApiGatewayRequestEventUtil.getCallingCognitoUsername; import static com.formkiq.aws.services.lambda.ApiResponseStatus.SC_OK; -import static com.formkiq.aws.services.lambda.services.ConfigService.MAX_WEBHOOKS; -import static com.formkiq.aws.services.lambda.services.ConfigService.WEBHOOK_TIME_TO_LIVE; +import static com.formkiq.stacks.dynamodb.ConfigService.MAX_WEBHOOKS; +import static com.formkiq.stacks.dynamodb.ConfigService.WEBHOOK_TIME_TO_LIVE; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.util.Collection; @@ -46,10 +47,10 @@ import com.formkiq.aws.services.lambda.ApiRequestHandlerResponse; import com.formkiq.aws.services.lambda.exceptions.BadException; import com.formkiq.aws.services.lambda.exceptions.TooManyRequestsException; -import com.formkiq.aws.services.lambda.services.ConfigService; import com.formkiq.aws.ssm.SsmService; import com.formkiq.module.lambdaservices.AwsServiceCache; import com.formkiq.stacks.api.CoreAwsServiceCache; +import com.formkiq.stacks.dynamodb.ConfigService; /** {@link ApiGatewayRequestHandler} for "/webhooks". */ public class WebhooksRequestHandler diff --git a/lambda-api/src/main/java/com/formkiq/stacks/api/handler/WebhooksTagsRequestHandler.java b/lambda-api/src/main/java/com/formkiq/stacks/api/handler/WebhooksTagsRequestHandler.java index e3dc04af6..589bc34ad 100644 --- a/lambda-api/src/main/java/com/formkiq/stacks/api/handler/WebhooksTagsRequestHandler.java +++ b/lambda-api/src/main/java/com/formkiq/stacks/api/handler/WebhooksTagsRequestHandler.java @@ -23,6 +23,7 @@ */ package com.formkiq.stacks.api.handler; +import static com.formkiq.aws.services.lambda.ApiGatewayRequestEventUtil.getCallingCognitoUsername; import static com.formkiq.aws.services.lambda.ApiResponseStatus.SC_CREATED; import static com.formkiq.aws.services.lambda.ApiResponseStatus.SC_OK; import java.util.Arrays; diff --git a/lambda-api/src/main/resources/cloudformation/api-apikey.yaml b/lambda-api/src/main/resources/cloudformation/api-apikey.yaml new file mode 100644 index 000000000..834d905b5 --- /dev/null +++ b/lambda-api/src/main/resources/cloudformation/api-apikey.yaml @@ -0,0 +1,3974 @@ +#@ load("@ytt:data", "data") +#@ load("@ytt:assert", "assert") +#@ load("@ytt:overlay", "overlay") +#@overlay/match by=overlay.all +--- +Resources: + + #@overlay/match missing_ok=True + DocumentsKeyApi: + Type: AWS::ApiGatewayV2::Api + Properties: + Body: + openapi: "3.0.1" + info: + title: + Fn::Sub: "${AWS::StackName} API Key - ${AppEnvironment}" + contact: + name: FormKiQ + url: https://formkiq.com + email: support@formkiq.com + x-logo: + url: https://docs.formkiq.com/docs/latest/_images/formkiq-logo.png + backgroundColor: "#FFFFFF" + altText: "FormKiQ Logo" + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html + description: "FormKiQ Api Key" + version: #@ data.values.version or assert.fail("missing version") + paths: + /version: + get: + operationId: GetVersion + description: Return the version of FormKiQ + tags: + - System Management + responses: + '200': + description: "200 OK" + headers: + Access-Control-Allow-Origin: + schema: + type: "string" + Access-Control-Allow-Methods: + schema: + type: "string" + Access-Control-Allow-Headers: + schema: + type: "string" + content: + application/json: + schema: + "$ref": "#/components/schemas/GetVersionRequest" + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + /sites: + get: + operationId: GetSites + description: Returns the list of sites that the user has access to + tags: + - System Management + responses: + '200': + description: "200 OK" + headers: + Access-Control-Allow-Origin: + schema: + type: "string" + Access-Control-Allow-Methods: + schema: + type: "string" + Access-Control-Allow-Headers: + schema: + type: "string" + content: + application/json: + schema: + "$ref": "#/components/schemas/GetSitesRequest" + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + /configuration: + get: + operationId: GetConfigs + description: Returns the list of sites that the user has access to + tags: + - System Management + parameters: + - $ref: '#/components/parameters/siteIdParam' + responses: + '200': + description: "200 OK" + headers: + Access-Control-Allow-Origin: + schema: + type: "string" + Access-Control-Allow-Methods: + schema: + type: "string" + Access-Control-Allow-Headers: + schema: + type: "string" + content: + application/json: + schema: + "$ref": "#/components/schemas/GetConfigurationResponse" + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + patch: + operationId: UpdateConfig + description: Update the System Management configuration + tags: + - System Management + parameters: + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SetConfigRequest' + examples: + Config: + value: + chatGptApiKey: "ABC" + maxContentLengthBytes: "1000000" + maxDocuments: "1000" + maxWebhooks: "10" + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/SetConfigResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + /configuration/apiKeys: + get: + operationId: GetApiKeys + description: Returns the list of ApiKeys + tags: + - System Management + parameters: + - $ref: '#/components/parameters/siteIdParam' + responses: + '200': + description: "200 OK" + headers: + Access-Control-Allow-Origin: + schema: + type: "string" + Access-Control-Allow-Methods: + schema: + type: "string" + Access-Control-Allow-Headers: + schema: + type: "string" + content: + application/json: + schema: + "$ref": "#/components/schemas/GetApiKeysResponse" + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + post: + operationId: Ã…ddApiKey + description: Adds a new API Key + tags: + - System Management + parameters: + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AddApiKeyRequest' + examples: + AddApiKey: + value: + name: My API Key + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/AddApiKeyResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + /configuration/apiKeys/{apiKey}: + delete: + operationId: DeleteApiKey + description: Adds a new API Key + tags: + - System Management + parameters: + - $ref: '#/components/parameters/apiKeyParam' + - $ref: '#/components/parameters/siteIdParam' + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/DeleteApiKeyResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + /tagSchemas: + get: + operationId: GetTagSchemas + description: Returns the list of tagSchemas; ONLY available with FormKiQ Pro and Enterprise + tags: + - Tag Schema + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/limitParam' + - name: "next" + in: "query" + description: "Next page of results token" + schema: + type: "string" + - name: "previous" + in: "query" + description: "Previous page of results token" + schema: + type: "string" + responses: + '200': + description: "200 OK" + headers: + Access-Control-Allow-Origin: + schema: + type: "string" + Access-Control-Allow-Methods: + schema: + type: "string" + Access-Control-Allow-Headers: + schema: + type: "string" + content: + application/json: + schema: + "$ref": "#/components/schemas/GetTagSchemasRequest" + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + post: + operationId: AddTagSchema + description: Creates a new TagSchema; ONLY available with FormKiQ Pro and Enterprise + tags: + - Tag Schema + parameters: + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AddTagSchemaRequest' + responses: + '201': + description: 201 CREATED + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/TagSchemaPostResponse' + '400': + description: 400 BAD REQUEST + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorsResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi201" + /tagSchemas/{tagSchemaId}: + get: + operationId: GetTagSchema + description: Retrieves a TagSchema's details, i.e., metadata; ONLY available with FormKiQ Pro and Enterprise + tags: + - Tag Schema + parameters: + - $ref: '#/components/parameters/tagSchemaIdParam' + - $ref: '#/components/parameters/siteIdParam' + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/GetTagSchemaRequest' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + delete: + operationId: DeleteTagSchema + description: Delete a TagSchema; ONLY available with FormKiQ Pro and Enterprise + tags: + - Tag Schema + parameters: + - $ref: '#/components/parameters/tagSchemaIdParam' + - $ref: '#/components/parameters/siteIdParam' + responses: + '200': + $ref: '#/components/responses/200Cors' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + /documents: + get: + operationId: GetDocuments + tags: + - Documents + description: Returns a list of the most recent documents added, ordered by inserted, descending + parameters: + - name: "date" + in: "query" + description: "Fetch documents inserted on a certain date (yyyy-MM-dd)" + schema: + type: "string" + - name: "tz" + in: "query" + description: "UTC offset to apply to date parameter (IE: -0600)" + schema: + type: "string" + - name: "next" + in: "query" + description: "Next page of results token" + schema: + type: "string" + - name: "previous" + in: "query" + description: "Previous page of results token" + schema: + type: "string" + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/limitParam' + responses: + '200': + description: "200 OK" + headers: + Access-Control-Allow-Origin: + schema: + type: "string" + Access-Control-Allow-Methods: + schema: + type: "string" + Access-Control-Allow-Headers: + schema: + type: "string" + content: + application/json: + schema: + "$ref": "#/components/schemas/GetDocumentsResponse" + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + post: + operationId: AddDocument + description: Creates a new document; body may include document content if less than 5 MB + tags: + - Documents + parameters: + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AddDocumentRequest' + examples: + Simple_File: + value: + path: test.txt + contentType: text/plain + isBase64: false + content: This is sample data file + tags: + - key: category + value: sample + - key: players + values: ["111", "222"] + metadata: + - key: info + value: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Nested_Document: + value: + path: doc1.txt + contentType: text/plain + content: This is document1 content + tags: + - key: type + value: document1 + documents: + contentType: text/plain, + content: This is sub document1 content, + tags: + - key: type + value: subdocument1 + Document_with_Webhook: + value: + path: test.txt + contentType: text/plain + isBase64: false + content: This is sample data file + actions: + - type: webhook + parameters: + url: + responses: + '201': + description: 201 CREATED + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/AddDocumentResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi201" + /documents/{documentId}: + get: + operationId: GetDocument + description: Retrieves a document's details, i.e., metadata + tags: + - Documents + parameters: + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/siteIdParam' + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/GetDocumentResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + patch: + operationId: UpdateDocument + description: Update a document's details, i.e., metadata + tags: + - Documents + parameters: + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AddDocumentRequest' + examples: + Simple_File: + value: + path: test.txt + contentType: text/plain + isBase64: false + content: This is sample data file + tags: + - key: category + value: sample + - key: players + values: ["111", "222"] + metadata: + - key: info + value: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/AddDocumentResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + delete: + operationId: DeleteDocument + description: Delete a document + tags: + - Documents + parameters: + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/siteIdParam' + responses: + '200': + $ref: '#/components/responses/200Cors' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + /documents/{documentId}/versions: + get: + operationId: GetDocumentVersions + description: Get a listing of document content and metadata versions; ONLY available with FormKiQ Pro and Enterprise + tags: + - Document Versions + parameters: + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/siteIdParam' + - name: next + in: query + description: "Next page of results token" + schema: + type: string + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/GetDocumentVersionsResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + put: + operationId: SetDocumentVersion + description: Set document to a previous document version; ONLY available with FormKiQ Pro and Enterprise + tags: + - Document Versions + parameters: + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SetDocumentVersionRequest' + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/SetDocumentVersionResponse' + '400': + description: 400 OK + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationErrorsResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + /documents/{documentId}/versions/{versionKey}: + delete: + operationId: DeleteDocumentVersion + description: Delete a specific previous document version; ONLY available with FormKiQ Pro and Enterprise + tags: + - Document Versions + parameters: + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/versionKeyPath' + responses: + '200': + $ref: '#/components/responses/200Cors' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + /documents/{documentId}/content: + get: + operationId: GetDocumentContent + description: Get a document's contents. Certain content types, text/*, application/json, and application/x-www-form-urlencoded. return the "content" field, while all other content types return a 'contentUrl' for retrieving the content. + tags: + - Documents + parameters: + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/versionKeyParam' + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + Location: + $ref: '#/components/headers/Location' + content: + application/json: + schema: + $ref: '#/components/schemas/GetDocumentContentResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + /documents/{documentId}/tags: + get: + operationId: GetDocumentTags + description: Get a listing of a document's tags + tags: + - Document Tags + parameters: + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/limitParam' + - name: next + in: query + description: "Next page of results token" + schema: + type: string + - name: previous + in: query + description: "Previous page of results token" + schema: + type: string + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/GetDocumentTagsResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + post: + operationId: AddDocumentTag + description: Add a single tag to a document; this endpoint also accepts a different body parameter for adding multiple tags in a single request + tags: + - Document Tags + parameters: + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AddDocumentTagRequest' + examples: + Add_Key_Only_Tag: + value: + key: category + Add_Key_and_Value_Tag: + value: + key: category + value: person + Add_Key_and_Values_Tag: + value: + key: category + values: [person,canadian] + responses: + '201': + $ref: '#/components/responses/200Cors' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi201" + /documents/{documentId}/tags#: + post: + operationId: AddDocumentTags + description: Add multiple tags to a document; this endpoint also accepts a different body parameter for adding a single tag + tags: + - Document Tags + parameters: + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AddDocumentTagsRequest' + responses: + '201': + $ref: '#/components/responses/200Cors' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi201" + /documents/{documentId}/tags/{tagKey}: + get: + operationId: GetDocumentTag + description: Get a document tag by using its key + tags: + - Document Tags + parameters: + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/tagkeyParam' + - $ref: '#/components/parameters/siteIdParam' + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/GetDocumentTagResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + put: + operationId: SetDocumentTag + description: Update any and all values of a document tag, by using its key; you can supply one tag value or a list of tag values in the request body + tags: + - Document Tags + parameters: + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/tagkeyParam' + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SetDocumentTagKeyRequest' + responses: + '200': + $ref: '#/components/responses/200Cors' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + delete: + operationId: DeleteDocumentTag + description: Delete a document tag by using its key + tags: + - Document Tags + parameters: + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/tagkeyParam' + - $ref: '#/components/parameters/siteIdParam' + responses: + '200': + $ref: '#/components/responses/200Cors' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + /documents/{documentId}/tags/{tagKey}/{tagValue}: + delete: + operationId: DeleteDocumentTagAndValue + description: Delete a specific document tag's key/value combination; the request will be ignored if there is no valid key/value combination found + tags: + - Document Tags + parameters: + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/tagkeyParam' + - $ref: '#/components/parameters/tagvalueParam' + - $ref: '#/components/parameters/siteIdParam' + responses: + '200': + $ref: '#/components/responses/200Cors' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + /documents/{documentId}/url: + get: + operationId: GetDocumentUrl + description: Returns a URL for the document's contents; this URL will expire (the default is 48 hours) + tags: + - Documents + parameters: + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/versionKeyParam' + - $ref: '#/components/parameters/durationParam' + - name: "inline" + in: "query" + description: "Set the Content-Disposition to inline" + schema: + type: boolean + default: false + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/GetDocumentUrlRequest' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + /documents/upload: + get: + operationId: GetDocumentUpload + description: Returns a URL that can be used to upload document content and create a new document; this endpoint (whether GET or POST) is required in order to add content that is larger than 5 MB + tags: + - Documents + parameters: + - name: path + in: query + description: The upload file's path + schema: + type: string + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/contentLengthParam' + - $ref: '#/components/parameters/durationParam' + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/GetDocumentUrlRequest' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + post: + operationId: AddDocumentUpload + description: Returns a URL that can be used to upload document content and create a new document, while allowing metadata to also be sent; this endpoint (whether GET or POST) is required in order to add content that is larger than 5 MB + tags: + - Documents + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/durationParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AddDocumentUploadRequest' + examples: + Simple_File: + value: + path: test.txt + contentType: text/plain + isBase64: false + content: This is sample data file + tags: + - key: category + value: sample + - key: players + values: ["111", "222"] + metadata: + - key: info + value: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + responses: + '201': + description: 201 CREATED + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/AddDocumentResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi201" + /documents/{documentId}/upload: + get: + operationId: GetDocumentIdUpload + parameters: + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/contentLengthParam' + - $ref: '#/components/parameters/durationParam' + description: Returns a URL that can be used to upload documents for a specific documentId; this endpoint is required in order to add content that is larger than 5 MB. If versions are enabled, this will create a new version of the document. + tags: + - Documents + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/GetDocumentUrlRequest' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + /search: + post: + operationId: DocumentSearch + description: Document search query request; documents are searched primarily using a document tag key and optional tag value, or if Typesense is enabled, searches on the document path and versioned metadata is also available. An optional documentIds parameter is also available in the DocumentSearchBody to filter, with up to 100 documentIds accepted. + tags: + - Document Search + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/limitParam' + - name: next + in: query + description: "Next page of results token" + schema: + type: string + - name: previous + in: query + description: "Previous page of results token" + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentSearchRequest' + examples: + Search_By_Key: + value: + query: + tag: + key: category + Search_By_Value: + value: + query: + tag: + key: category + eq: person + Tag_Value_Begins_With: + value: + query: + tag: + key: firstname + beginsWith: jo + Search_By_Value_for_Specific_Documents: + value: + query: + tag: + key: category + eq: person + documentIds: ["1", "2"] + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentSearchResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/searchLambdaApi200" + /searchFulltext: + post: + operationId: DocumentFulltext + description: Document full-text search (and more robust multi-tag search queries, powered by OpenSearch); ONLY available with FormKiQ Enterprise + tags: + - Advanced Document Search + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/limitParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentFulltextRequest' + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentFulltextResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200" + /queryFulltext: + post: + operationId: QueryFulltext + description: Endpoint for allowing custom, complex queries using the OpenSearch search API (https://opensearch.org/docs/latest/opensearch/rest-api/search/); ONLY available with FormKiQ Enterprise + tags: + - Advanced Document Search + parameters: + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/QueryFulltextRequest' + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/QueryFulltextResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200" + /documents/{documentId}/actions: + get: + operationId: GetDocumentActions + description: Get document actions and their status + tags: + - Document Actions + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/documentIdParam' + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/GetDocumentActionsResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + post: + operationId: AddDocumentActions + description: Add one or more actions to a document; this appends actions and does not replace previous actions + tags: + - Document Actions + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/documentIdParam' + requestBody: + required: false + content: + application/json: + schema: + $ref: '#/components/schemas/AddDocumentActionsRequest' + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/AddDocumentActionsResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + /documents/{documentId}/ocr: + get: + operationId: GetDocumentOcr + description: Get a document's optical character recognition (OCR) result, if exists; ONLY available with FormKiQ Pro and Enterprise + tags: + - OCR + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/contentUrlParam' + - $ref: '#/components/parameters/textParam' + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/GetDocumentOcrResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/ocrLambdaApi200" + post: + operationId: AddDocumentOcr + description: Document optical character recognition (OCR) request; extract text and data from a document; ONLY available with FormKiQ Pro and Enterprise + tags: + - OCR + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/documentIdParam' + requestBody: + required: false + content: + application/json: + schema: + $ref: '#/components/schemas/AddDocumentOcrRequest' + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/AddDocumentOcrResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/ocrLambdaApi200" + put: + operationId: SetDocumentOcr + description: Set a document's optical character recognition (OCR) result for a document; ONLY available with FormKiQ Pro and Enterprise + tags: + - OCR + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/documentIdParam' + requestBody: + required: false + content: + application/json: + schema: + $ref: '#/components/schemas/SetDocumentOcrRequest' + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/AddDocumentOcrResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/ocrLambdaApi200" + delete: + operationId: DeleteDocumentOcr + description: Delete a document's optical character recognition (OCR) result, if exists; ONLY available with FormKiQ Pro and Enterprise + tags: + - OCR + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/documentIdParam' + responses: + '200': + $ref: '#/components/responses/200Cors' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/ocrLambdaApi200" + /documents/{documentId}/antivirus: + put: + operationId: SetAntivirus + description: Perform an Anti-Malware / Antivirus scan on a document; ONLY available with FormKiQ Pro and Enterprise + tags: + - Antivirus + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/documentIdParam' + requestBody: + required: false + content: + application/json: + schema: + $ref: '#/components/schemas/SetAntivirusRequest' + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/SetAntivirusResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/antivirusLambdaApi200" + /documents/{documentId}/fulltext: + get: + operationId: GetDocumentFulltext + description: Retrieve an OpenSearch document's details, i.e., metadata + tags: + - Advanced Document Search + parameters: + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/siteIdParam' + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/GetDocumentFulltextResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200" + delete: + operationId: DeleteDocumentFulltext + description: Remove full text search for a document from OpenSearch; ONLY available with FormKiQ Enterprise + tags: + - Advanced Document Search + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/documentIdParam' + responses: + '200': + $ref: '#/components/responses/200Cors' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200" + put: + operationId: SetDocumentFulltext + description: Set all text/tags found within a document to OpenSearch; ONLY available with FormKiQ Enterprise + tags: + - Advanced Document Search + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/documentIdParam' + requestBody: + required: false + content: + application/json: + schema: + $ref: '#/components/schemas/SetDocumentFulltextRequest' + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/SetDocumentFulltextResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200" + patch: + operationId: UpdateDocumentFulltext + description: Update a document in OpenSearch; ONLY available with FormKiQ Enterprise + tags: + - Advanced Document Search + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/documentIdParam' + requestBody: + required: false + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateDocumentFulltextRequest' + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/SetDocumentFulltextResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200" + /documents/{documentId}/fulltext/tags/{tagKey}: + delete: + operationId: DeleteDocumentFulltextTag + description: Remove document tags from OpenSearch; ONLY available with FormKiQ Enterprise + tags: + - Advanced Document Search + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/tagkeyParam' + responses: + '200': + $ref: '#/components/responses/200Cors' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200" + /documents/{documentId}/fulltext/tags/{tagKey}/{tagValue}: + delete: + operationId: DeleteDocumentFulltextTagAndValue + description: Remove document tag/value from OpenSearch; ONLY available with FormKiQ Enterprise + tags: + - Advanced Document Search + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/tagkeyParam' + - $ref: '#/components/parameters/tagvalueParam' + responses: + '200': + $ref: '#/components/responses/200Cors' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200" + /documents/{documentId}/syncs: + get: + operationId: GetDocumentSyncs + description: Retrieve the document syncs with external services status + tags: + - Document Synchronization + parameters: + - $ref: '#/components/parameters/documentIdParam' + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/limitParam' + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/GetDocumentSyncResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + /public/documents: + post: + operationId: PublicAddDocument + description: Allow unauthenticated creation of new documents; must be enabled during installation (disabled by default) + tags: + - Public + parameters: + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AddDocumentRequest' + responses: + '201': + description: 201 CREATED + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentId' + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi201" + /public/webhooks/{webhooks+}: + post: + operationId: PublicAddWebhook + description: Receive an incoming public post to a specified webhook and creates a document based on the data sent; must be enabled during installation (disabled by default) + tags: + - Public + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/webhooksParam' + requestBody: + required: true + content: + application/json: + schema: + type: object + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentId' + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + /private/webhooks/{webhooks+}: + post: + operationId: AddWebhookDocument + description: Receive an incoming private webhook and creates a document based on the webhook's body; requires authentication + tags: + - Webhooks + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/webhooksParam' + requestBody: + required: true + content: + application/json: + schema: + type: object + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentId' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + /webhooks: + get: + operationId: GetWebhooks + tags: + - Webhooks + description: Return a list of webhooks; each webhook's id can be provided to an external service, allowing data to be sent, received, and processed via that webhook + parameters: + - $ref: '#/components/parameters/siteIdParam' + responses: + '200': + description: "200 OK" + headers: + Access-Control-Allow-Origin: + schema: + type: "string" + Access-Control-Allow-Methods: + schema: + type: "string" + Access-Control-Allow-Headers: + schema: + type: "string" + content: + application/json: + schema: + "$ref": "#/components/schemas/GetWebhooksResponse" + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + post: + operationId: AddWebhook + description: Create a new webhook; once created, a webhook's id can be provided to an external service, allowing data to be sent, received, and processed via that webhook + tags: + - Webhooks + parameters: + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AddWebhookRequest' + responses: + '201': + description: 201 CREATED + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/AddWebhookResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi201" + /webhooks/{webhookId}: + get: + operationId: GetWebhook + tags: + - Webhooks + description: Return a webhook's details, i.e., its metadata + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/webhookIdParam' + responses: + '200': + description: "200 OK" + headers: + Access-Control-Allow-Origin: + schema: + type: "string" + Access-Control-Allow-Methods: + schema: + type: "string" + Access-Control-Allow-Headers: + schema: + type: "string" + content: + application/json: + schema: + "$ref": "#/components/schemas/GetWebhookResponse" + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + delete: + operationId: DeleteWebhook + description: Delete a webhook; this will disable sending, receiving, or processing of data from external services to this webhook + tags: + - Webhooks + parameters: + - $ref: '#/components/parameters/webhookIdParam' + - $ref: '#/components/parameters/siteIdParam' + responses: + '200': + $ref: '#/components/responses/200Cors' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + patch: + operationId: UpdateWebhook + description: Updates a webhook's details, i.e., its metadata + tags: + - Webhooks + parameters: + - $ref: '#/components/parameters/webhookIdParam' + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AddWebhookRequest' + responses: + '200': + $ref: '#/components/responses/200Cors' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + /webhooks/{webhookId}/tags: + get: + operationId: GetWebhookTags + description: Get a webhook's tags + tags: + - Webhooks + parameters: + - $ref: '#/components/parameters/webhookIdParam' + - $ref: '#/components/parameters/siteIdParam' + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/GetWebhookTagsResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + post: + operationId: AddWebhookTag + description: Add a tag to a webhook + tags: + - Webhooks + parameters: + - $ref: '#/components/parameters/webhookIdParam' + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/GetDocumentTagResponse' + examples: + Add_Key_Only_Tag: + value: + key: category + Add_Key_and_Value_Tag: + value: + key: category + value: person + responses: + '201': + $ref: '#/components/responses/200Cors' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi201" + /onlyoffice/{documentId}/edit: + post: + operationId: OnlyOfficeDocumentEdit + description: Provide ONLYOFFICE integration for editing documents; ONLY available with FormKiQ Enterprise + tags: + - Onlyoffice + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/documentIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/OnlyOfficeDocumentEditRequest' + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/OnlyOfficeDocumentResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/onlyOfficeLambdaApi200" + /onlyoffice/new: + post: + operationId: OnlyOfficeDocumentNew + description: Provide ONLYOFFICE integration for the creation of new documents; ONLY available with FormKiQ Enterprise + tags: + - Onlyoffice + parameters: + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/OnlyOfficeDocumentNewRequest' + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/OnlyOfficeDocumentResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/onlyOfficeLambdaApi200" + /onlyoffice/{documentId}/save: + post: + operationId: OnlyOfficeDocumentSave + description: Save an updated document for ONLYOFFICE integration. ONLY available with FormKiQ Enterprise + tags: + - Onlyoffice + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/documentIdParam' + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/OnlyOfficeDocumentSaveResponse' + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/onlyOfficeLambdaApi200" + /indices/{indexType}/move: + post: + operationId: IndexFolderMove + description: Perform an Folder Index Move + tags: + - Folder Index + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/indexTypeParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/IndexFolderMoveRequest' + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/IndexFolderMoveResponse' + '400': + description: 400 OK + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationErrorsResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + /indices/{indexType}/{indexKey}: + delete: + operationId: DeleteIndex + description: Perform a delete on the Folder Index + tags: + - Folder Index + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/indexKeyParam' + - $ref: '#/components/parameters/indexTypeParam' + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + '400': + description: 400 OK + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationErrorsResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + /indices/search: + post: + operationId: IndexSearch + description: Perform a search on a index; this is currently available for both folder and tag indices + tags: + - Folder Index + - Tag Index + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/limitParam' + - name: next + in: query + description: "Next page of results token" + schema: + type: string + - name: previous + in: query + description: "Previous page of results token" + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/IndexSearchRequest' + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/IndexSearchResponse' + '400': + description: 400 OK + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationErrorsResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + /esignature/docusign/{documentId}: + post: + operationId: EsignatureDocusign + description: Create a DocuSign E-Signature request; ONLY available with FormKiQ Enterprise + tags: + - E-Signature + parameters: + - $ref: '#/components/parameters/siteIdParam' + - $ref: '#/components/parameters/documentIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/EsignatureDocusignRequest' + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/EsignatureDocusignResponse' + '400': + description: 400 OK + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationErrorsResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/esignatureLambdaApi200" + /esignature/docusign/config: + get: + operationId: EsignatureDocusignConfig + description: Get DocuSign configuration info; ONLY available with FormKiQ Enterprise + tags: + - E-Signature + parameters: + - $ref: '#/components/parameters/siteIdParam' + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/EsignatureDocusignConfigResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/esignatureLambdaApi200" + put: + operationId: EsignatureSetDocusignConfig + description: Set DocuSign configuration, required for integration; ONLY available with FormKiQ Enterprise + tags: + - E-Signature + parameters: + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/EsignatureSetDocusignConfigRequest' + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/EsignatureSetDocusignConfigResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/esignatureLambdaApi200" + /esignature/docusign/events: + post: + operationId: AddEsignatureDocusignEvents + description: DocuSign callback URL handler; ONLY available with FormKiQ Enterprise + tags: + - E-Signature + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/EsignatureDocusignResponse' + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/esignatureLambdaApi200" + components: + headers: + AccessControlAllowOrigin: + schema: + type: string + AccessControlAllowMethods: + schema: + type: string + AccessControlAllowHeaders: + schema: + type: string + Location: + schema: + type: string + responses: + 200Cors: + description: 200 OK + headers: + Access-Control-Allow-Origin: + schema: + type: string + Access-Control-Allow-Methods: + schema: + type: string + Access-Control-Allow-Headers: + schema: + type: string + content: {} + parameters: + versionKeyPath: + name: versionKey + in: path + description: Version Key + required: true + schema: + type: string + versionKeyParam: + name: versionKey + in: query + description: Version Key + required: false + schema: + type: string + webhooksParam: + name: webhooks+ + in: path + required: true + description: Web Hook Param + schema: + type: string + webhookIdParam: + name: webhookId + in: path + required: true + description: Web Hook Param + schema: + type: string + siteIdParam: + name: siteId + in: query + description: Site Identifier + required: false + schema: + type: string + providerParam: + name: provider + in: query + description: Provider Identifier + required: true + schema: + type: string + limitParam: + name: limit + in: query + description: Limit Results + required: false + schema: + type: string + default: "10" + apiKeyParam: + name: apiKey + in: path + description: API Key + required: true + schema: + type: string + documentIdParam: + name: documentId + in: path + description: Document Identifier + required: true + schema: + type: string + tagSchemaIdParam: + name: tagSchemaId + in: path + description: Tag Schema Identifier + required: true + schema: + type: string + tagkeyParam: + name: tagKey + in: path + description: Tag Key + required: true + schema: + type: string + tagvalueParam: + name: tagValue + in: path + description: Tag Key Value + required: true + schema: + type: string + contentLengthParam: + name: contentLength + in: query + description: Indicates the size of the entity-body + required: false + schema: + type: integer + durationParam: + name: duration + in: query + description: Indicates the number of hours request is valid for + schema: + type: integer + contentUrlParam: + name: contentUrl + in: query + description: Whether to return a "contentUrl", set value to 'true' + required: false + schema: + type: string + textParam: + name: text + in: query + description: Returns raw 'text' of OCR content. e.g. AWS Textract returns JSON, setting parameter to 'true' converts JSON to Text + required: false + schema: + type: string + indexKeyParam: + name: indexKey + in: path + description: Index Key Identifier + required: true + schema: + type: string + indexTypeParam: + name: indexType + in: path + description: Index Type + required: true + schema: + type: string + schemas: + ValidationErrorsResponse: + type: "object" + properties: + errors: + $ref: "#/components/schemas/ValidationErrors" + ValidationErrors: + type: "array" + description: "List of errors" + items: + $ref: "#/components/schemas/ValidationError" + ValidationError: + type: "object" + properties: + key: + type: "string" + description: "Error Key" + error: + type: "string" + description: "Error Message" + IndexFolderMoveRequest: + type: "object" + properties: + source: + type: "string" + description: "Source path" + target: + type: "string" + description: "Target path" + SetDocumentTagKeyRequest: + type: "object" + properties: + value: + type: "string" + description: "Tag value" + values: + type: "array" + description: "Tag values" + items: + type: "string" + IndexFolderMoveResponse: + type: "object" + properties: + message: + type: "string" + description: "Folder move message" + IndexSearchRequest: + type: "object" + properties: + indexType: + type: "string" + description: "The name of the index to search" + IndexSearchResponse: + type: "object" + properties: + next: + type: "string" + description: "Next page of results token" + previous: + type: "string" + description: "Previous page of results token" + values: + $ref: "#/components/schemas/IndexSearchItems" + IndexSearchItems: + type: "array" + description: "List of search results" + items: + $ref: "#/components/schemas/IndexSearch" + IndexSearch: + type: "object" + properties: + value: + type: "string" + description: "value of index" + GetDocumentContentResponse: + type: "object" + properties: + content: + type: "string" + description: "Document content" + contentUrl: + type: "string" + description: "URL to retrieve document content" + contentType: + type: "string" + description: "Document Content-Type" + isBase64: + type: "boolean" + description: "Is the content Base64-encoded?" + GetDocumentVersionsResponse: + type: "object" + properties: + next: + type: "string" + description: "Next page of results token" + versions: + $ref: "#/components/schemas/DocumentItemVersions" + GetDocumentTagsResponse: + type: "object" + properties: + next: + type: "string" + description: "Next page of results token" + previous: + type: "string" + description: "Previous page of results token" + tags: + $ref: "#/components/schemas/DocumentItemTags" + SetDocumentVersionRequest: + type: "object" + properties: + versionKey: + type: "string" + description: "VersionKey returned by the GET /documents/{documentId}/versions to revert to" + SetDocumentVersionResponse: + type: "object" + properties: + message: + type: "string" + description: "Response Message" + DocumentItemTags: + type: "array" + description: "List of tags" + items: + $ref: "#/components/schemas/GetDocumentTagResponse" + DocumentItemVersions: + type: "array" + description: "List of document versions" + items: + $ref: "#/components/schemas/DocumentItemVersion" + DocumentSearch: + type: "object" + description: "Document tag search criteria" + properties: + text: + type: "string" + description: "Full text search" + meta: + $ref: "#/components/schemas/DocumentSearchItemMeta" + tag: + $ref: "#/components/schemas/DocumentSearchItemTag" + documentIds: + type: array + description: "List of DocumentIds to filter search results on" + items: + type: "string" + DocumentId: + required: + - "documentId" + type: "object" + properties: + documentId: + type: "string" + format: "uuid" + description: "Document Identifier" + siteId: + type: "string" + description: "Site Identifier" + AddWebhookResponse: + type: "object" + properties: + id: + type: "string" + format: "uuid" + siteId: + type: "string" + description: "Site Identifier" + AddDocumentResponse: + type: "object" + properties: + documentId: + type: "string" + format: "uuid" + description: "Document Identifier" + siteId: + type: "string" + description: "Site Identifier" + uploadUrl: + type: "string" + description: "Url to upload document to" + documents: + $ref: "#/components/schemas/AddChildDocumentsResponse" + AddChildDocumentsResponse: + type: "array" + description: "List of child documents" + items: + $ref: "#/components/schemas/AddChildDocumentResponse" + AddChildDocumentResponse: + type: "object" + properties: + documentId: + type: "string" + description: "Document Identifier" + uploadUrl: + type: "string" + description: "Url to upload document to" + GetDocumentUrlRequest: + type: "object" + properties: + documentId: + type: "string" + description: "Document Identifier" + url: + type: "string" + description: "Document content url" + GetDocumentResponse: + required: + - "documentId" + - "path" + type: "object" + properties: + next: + type: "string" + description: "Next page of results token" + previous: + type: "string" + description: "Previous page of results token" + siteId: + type: "string" + description: "Site Identifier" + path: + type: "string" + description: "Path or Name of document" + insertedDate: + type: "string" + format: "date-time" + description: "Inserted Timestamp" + lastModifiedDate: + type: "string" + format: "date-time" + description: "Last Modified Timestamp" + checksum: + type: "string" + description: "Document checksum, changes when document file changes" + documentId: + type: "string" + format: "uuid" + description: "Document Identifier" + contentType: + type: "string" + description: "Document Content-Type" + userId: + type: "string" + description: "User who added document" + contentLength: + type: "integer" + description: "Document size" + versionId: + type: "string" + description: "Document version" + belongsToDocumentId: + type: "string" + description: "Parent Document Identifier" + documents: + type: "array" + description: "List of related documents" + items: + type: "object" + properties: + path: + type: "string" + description: "Path or Name of document" + insertedDate: + type: "string" + format: "date-time" + description: "Inserted Timestamp" + lastModifiedDate: + type: "string" + format: "date-time" + description: "Last Modified Timestamp" + checksum: + type: "string" + description: "Document checksum, changes when document file changes" + documentId: + type: "string" + format: "uuid" + description: "Document Identifier" + contentType: + type: "string" + description: "Document Content-Type" + userId: + type: "string" + description: "User who added document" + contentLength: + type: "integer" + description: "Document size" + versionId: + type: "string" + description: "Document version" + belongsToDocumentId: + type: "string" + description: "Parent Document Identifier" + DocumentFulltextRequest: + required: + - "query" + type: "object" + description: "Document full text search" + properties: + query: + $ref: "#/components/schemas/DocumentFulltextSearch" + responseFields: + $ref: "#/components/schemas/SearchResponseFields" + DocumentFulltextSearch: + type: "object" + description: "Document full text search criteria" + properties: + page: + type: "integer" + description: "Result page to return (starting at 1)" + text: + type: "string" + description: "Full text search" + tags: + $ref: "#/components/schemas/DocumentFulltextTags" + DocumentFulltextTags: + type: "array" + description: "List of search tags" + items: + $ref: "#/components/schemas/DocumentFulltextTag" + DocumentFulltextTag: + required: + - "key" + type: "object" + properties: + eq: + type: "string" + description: "Searches for strings that eq" + eqOr: + type: array + description: "Searches for ANY strings that eq" + items: + type: "string" + key: + type: "string" + description: "Tag key to search" + DocumentSearchRequest: + required: + - "query" + type: "object" + description: "Document search tag criteria" + properties: + query: + $ref: "#/components/schemas/DocumentSearch" + responseFields: + $ref: "#/components/schemas/SearchResponseFields" + SearchResponseFields: + properties: + tags: + type: "array" + items: + type: "string" + DocumentSearchItemMeta: + type: "object" + properties: + folder: + type: "string" + description: "Searches for a folder" + path: + type: "string" + description: "Searches for a Path of document" + indexType: + type: "string" + description: "Searches in an index" + enum: + - folder + indexFilterBeginsWith: + type: "string" + description: "Returns index records that begins with a particular substring" + DocumentSearchItemTag: + required: + - "key" + type: "object" + properties: + beginsWith: + type: "string" + description: "Searches for strings that begin with" + eq: + type: "string" + description: "Searches for strings that eq" + eqOr: + type: array + description: "Searches for ANY strings that eq" + items: + type: "string" + key: + type: "string" + description: "Tag key to search" + AddWebhookRequest: + required: + - "name" + type: "object" + properties: + name: + type: "string" + description: "Name of webhook" + ttl: + type: "string" + description: "Webhook time to live (expiry)" + enabled: + type: "string" + description: "Is webhook enabled" + tags: + $ref: "#/components/schemas/AddDocumentTags" + AddDocumentUploadRequest: + required: + - "content" + type: "object" + properties: + tagSchemaId: + type: "string" + description: "Tag Schema Id" + path: + type: "string" + description: "Path or Name of document" + tags: + $ref: "#/components/schemas/AddDocumentTags" + actions: + $ref: "#/components/schemas/AddActions" + AddDocumentRequest: + required: + - "content" + type: "object" + properties: + tagSchemaId: + type: "string" + description: "Tag Schema Id" + path: + type: "string" + description: "Path or Name of document" + contentType: + type: "string" + description: "Document media type" + isBase64: + type: "boolean" + description: "Is the content Base64-encoded?" + content: + type: "string" + description: "Document content" + tags: + $ref: "#/components/schemas/AddDocumentTags" + metadata: + $ref: "#/components/schemas/AddDocumentMetadatas" + actions: + $ref: "#/components/schemas/AddActions" + documents: + $ref: "#/components/schemas/AddChildDocumentRequest" + AddDocumentTagsRequest: + type: "object" + description: "Add List of document tags" + properties: + tags: + $ref: "#/components/schemas/AddDocumentTags" + AddDocumentMetadatas: + type: "array" + description: "List of document Metadata" + items: + $ref: "#/components/schemas/AddDocumentMetadata" + AddDocumentMetadata: + required: + - "key" + type: "object" + description: "Document Metadata (use either 'value' or 'values' not both)" + properties: + key: + type: "string" + description: "Metadata key" + value: + type: "string" + description: "Metadata value" + values: + type: "array" + description: "Metadata values" + items: + type: "string" + description: "Metadata value" + AddDocumentTags: + type: "array" + description: "List of document tags" + items: + $ref: "#/components/schemas/AddDocumentTagRequest" + AddDocumentTagRequest: + required: + - "key" + type: "object" + description: "List of Document Tags (use either 'value' or 'values' not both)" + properties: + key: + type: "string" + description: "Tag key" + value: + type: "string" + description: "Tag value" + values: + type: "array" + description: "Tag values" + items: + type: "string" + description: "Tag value" + AddChildDocumentRequest: + type: "array" + description: "List of child documents" + items: + $ref: "#/components/schemas/AddChildDocument" + AddChildDocument: + required: + - "content" + type: "object" + description: "List of related documents" + properties: + path: + type: "string" + description: "Path or Name of document" + contentType: + type: "string" + description: "Document Content-Type" + isBase64: + type: "boolean" + description: "Is the content Base64-encoded?" + content: + type: "string" + description: "Document content" + DocumentItemVersion: + type: "object" + properties: + path: + type: "string" + description: "Path or Name of document" + insertedDate: + type: "string" + format: "date-time" + description: "Inserted Timestamp" + lastModifiedDate: + type: "string" + format: "date-time" + description: "Last Modified Timestamp" + checksum: + type: "string" + description: "Document checksum, changes when document file changes" + documentId: + type: "string" + format: "uuid" + description: "Document Identifier" + contentType: + type: "string" + description: "Document Content-Type" + userId: + type: "string" + description: "User who added document" + contentLength: + type: "integer" + description: "Document size" + versionId: + type: "string" + description: "Document version" + versionKey: + type: "string" + description: "Document Version Identifier" + GetDocumentTagResponse: + required: + - "key" + - "value" + type: "object" + properties: + insertedDate: + type: "string" + description: "Inserted Timestamp" + documentId: + type: "string" + description: "Document Identifier" + type: + type: "string" + description: "Tag type" + userId: + type: "string" + description: "User who added document" + value: + type: "string" + description: "Tag value" + values: + type: "array" + description: "Tag values" + items: + type: "string" + description: "Tag value" + key: + type: "string" + description: "Tag key" + QueryFulltextRequest: + description: OpenSearch search API request (https://opensearch.org/docs/latest/opensearch/rest-api/search/) + type: "object" + example: + query: + match: + title: "Wind" + QueryFulltextResponse: + type: "object" + properties: + result: + type: "object" + example: + result: + hits: + hits: + - _index: "" + _id: "" + _source: + documentId: "" + content: "" + DocumentFulltextResponse: + type: "object" + properties: + documents: + $ref: "#/components/schemas/FulltextSearchItems" + FulltextSearchItems: + type: "array" + description: "List of search result documents" + items: + $ref: "#/components/schemas/FulltextSearchItem" + FulltextSearchItem: + type: "object" + properties: + siteId: + type: "string" + description: "Site Identifier" + path: + type: "string" + description: "Path or Name of document" + insertedDate: + type: "string" + format: "date-time" + description: "Inserted Timestamp" + documentId: + type: "string" + format: "uuid" + description: "Document Identifier" + contentLength: + type: "integer" + description: "Document size" + createdBy: + type: "string" + description: "User who added document" + tags: + type: "object" + GetDocumentFulltextResponse: + type: "object" + properties: + siteId: + type: "string" + description: "Site Identifier" + content: + type: "string" + description: "Content of document" + path: + type: "string" + description: "Path or Name of document" + insertedDate: + type: "string" + format: "date-time" + description: "Inserted Timestamp" + documentId: + type: "string" + format: "uuid" + description: "Document Identifier" + createdBy: + type: "string" + description: "User who added document" + contentLength: + type: "integer" + description: "Document size" + tags: + type: "object" + metadata: + type: "object" + GetDocumentSyncResponse: + type: "object" + properties: + next: + type: "string" + description: "Next page of results token" + syncs: + $ref: "#/components/schemas/GetDocumentSyncs" + GetDocumentSyncs: + type: "array" + description: "List of document syncs" + items: + $ref: "#/components/schemas/GetDocumentSync" + GetDocumentSync: + type: "object" + properties: + service: + type: "string" + description: "To which service the data was synced" + enum: + - TYPESENSE + - OPENSEARCH + status: + type: "string" + description: "The status of the sync" + enum: + - COMPLETE + - FAILED + type: + type: "string" + description: "The type of the sync" + enum: + - METADATA + - TAG + syncDate: + type: "string" + format: "date-time" + description: "Timestamp of synchronization" + userId: + type: "string" + description: "User who added document" + message: + type: string + description: Document sync message + AddDocumentSyncRequest: + type: "object" + properties: + service: + type: "string" + description: "To which service to sync the data to" + enum: + - TYPESENSE + - OPENSEARCH + DocumentSearchResponse: + type: "object" + properties: + next: + type: "string" + description: "Next page of results token" + previous: + type: "string" + description: "Previous page of results token" + documents: + $ref: "#/components/schemas/SearchDocumentItems" + SearchDocumentItems: + type: "array" + description: "List of search result documents" + items: + $ref: "#/components/schemas/SearchDocumentItem" + SearchDocumentItem: + type: "object" + properties: + siteId: + type: "string" + description: "Site Identifier" + path: + type: "string" + description: "Path or Name of document" + insertedDate: + type: "string" + format: "date-time" + description: "Inserted Timestamp" + lastModifiedDate: + type: "string" + format: "date-time" + description: "Last Modified Timestamp" + folder: + type: "boolean" + description: "Is Result a Document Folder" + indexKey: + type: "string" + description: "populated if search result are from an index" + checksum: + type: "string" + description: "Document checksum, changes when document file changes" + documentId: + type: "string" + format: "uuid" + description: "Document Identifier" + contentType: + type: "string" + description: "Document Content-Type" + userId: + type: "string" + description: "User who added document" + contentLength: + type: "integer" + description: "Document size" + versionId: + type: "string" + description: "Document version" + matchedTag: + $ref: "#/components/schemas/DocumentSearchMatchTag" + tags: + type: "object" + DocumentSearchMatchTag: + type: "object" + properties: + key: + type: "string" + description: "Tag key" + value: + type: "string" + description: "Tag value" + type: + type: "string" + description: "Tag type" + GetDocumentsResponse: + type: "object" + properties: + next: + type: "string" + description: "Next page of results token" + previous: + type: "string" + description: "Previous page of results token" + documents: + $ref: "#/components/schemas/DocumentItemResults" + DocumentItemResults: + type: "array" + description: "List of documents" + items: + $ref: "#/components/schemas/DocumentItemResult" + DocumentItemResult: + type: "object" + properties: + siteId: + type: "string" + description: "Site Identifier" + path: + type: "string" + description: "Path or Name of document" + insertedDate: + type: "string" + format: "date-time" + description: "Inserted Timestamp" + lastModifiedDate: + type: "string" + format: "date-time" + description: "Last Modified Timestamp" + checksum: + type: "string" + description: "Document checksum, changes when document file changes" + documentId: + type: "string" + format: "uuid" + description: "Document Identifier" + contentType: + type: "string" + description: "Document Content-Type" + userId: + type: "string" + description: "User who added document" + contentLength: + type: "integer" + description: "Document size" + versionId: + type: "string" + description: "Document version" + GetWebhooksResponse: + type: "object" + properties: + webhooks: + $ref: "#/components/schemas/Webhooks" + Webhooks: + type: "array" + description: "List of webhooks" + items: + $ref: "#/components/schemas/GetWebhookResponse" + GetWebhookResponse: + type: "object" + properties: + siteId: + type: "string" + description: "Site Identifier" + name: + type: "string" + description: "Webhook name" + url: + type: "string" + description: "Webhook url" + insertedDate: + type: "string" + format: "date-time" + description: "Inserted Timestamp" + id: + type: "string" + format: "uuid" + description: "Webhook Identifier" + userId: + type: "string" + description: "User who added document" + GetWebhookTagsResponse: + type: "object" + properties: + next: + type: "string" + description: "Next page of results token" + previous: + type: "string" + description: "Previous page of results token" + tags: + $ref: "#/components/schemas/WebhookTagsList" + WebhookTagsList: + type: "array" + description: "List of webhook tags" + items: + $ref: "#/components/schemas/WebhookTag" + WebhookTag: + required: + - "key" + - "value" + type: "object" + properties: + insertedDate: + type: "string" + description: "Inserted Timestamp" + webhookId: + type: "string" + description: "Webhook Identifier" + type: + type: "string" + description: "Tag type" + userId: + type: "string" + description: "User who added document" + value: + type: "string" + description: "Tag value" + key: + type: "string" + description: "Tag key" + GetDocumentActionsResponse: + type: "object" + properties: + actions: + $ref: "#/components/schemas/DocumentActionList" + DocumentActionList: + type: "array" + description: "List of document actions" + items: + $ref: "#/components/schemas/DocumentAction" + DocumentAction: + type: object + properties: + status: + type: string + description: Status of the Document Action + type: + type: string + description: Type of Document Action + userId: + type: "string" + description: "User who requested the Action" + parameters: + type: "object" + description: Action parameters + GetDocumentOcrResponse: + type: object + properties: + data: + type: string + description: OCR text result + ocrEngine: + type: string + description: The OCR technique used + ocrStatus: + type: string + description: The status of the OCR request + contentType: + type: "string" + description: "Document Content-Type" + isBase64: + type: "boolean" + description: "Is the content Base64-encoded?" + userId: + type: "string" + description: "User who requested the OCR" + documentId: + type: "string" + format: "uuid" + description: "Document Identifier" + AddDocumentActionsResponse: + type: object + properties: + message: + type: string + description: Document Action message + AddDocumentOcrResponse: + type: object + properties: + message: + type: string + description: OCR processing message + SetDocumentFulltextResponse: + type: object + properties: + message: + type: string + description: Full text processing message + SetDocumentFulltextRequest: + type: object + properties: + contentType: + type: "string" + description: "Document Content-Type" + content: + type: "string" + description: "Document content" + contentUrls: + type: "array" + items: + type: "string" + description: "URL(s) which contain document content" + path: + type: "string" + description: "Path or Name of document" + tags: + $ref: "#/components/schemas/AddDocumentTags" + metadata: + $ref: "#/components/schemas/AddDocumentMetadatas" + UpdateDocumentFulltextRequest: + type: object + properties: + path: + type: "string" + description: "Path or Name of document" + content: + type: "string" + description: "Document content" + tags: + $ref: "#/components/schemas/AddDocumentTags" + SetAntivirusRequest: + type: object + SetAntivirusResponse: + type: object + properties: + message: + type: string + description: Antivirus processing message + AddDocumentActionsRequest: + type: object + properties: + actions: + $ref: "#/components/schemas/AddActions" + AddActions: + type: array + description: "List of Actions" + items: + $ref: "#/components/schemas/AddAction" + AddAction: + type: object + required: + - type + properties: + type: + type: "string" + description: Type of Document Action + enum: + - OCR + - FULLTEXT + - ANTIVIRUS + - WEBHOOK + - DOCUMENTTAGGING + parameters: + $ref: "#/components/schemas/AddActionParameters" + AddActionParameters: + type: "object" + properties: + ocrParseTypes: + type: "string" + description: "OCR: Parse types - TEXT, FORMS, TABLES" + addPdfDetectedCharactersAsText: + type: "boolean" + description: "OCR: For the rewriting of the PDF document, converting any image text to searchable text" + url: + type: "string" + description: "Webhook: Callback URL" + characterMax: + type: "string" + description: "Fulltext: Maximum number of characters (-1 unlimited, Typesense defaults to 2048 characters)" + engine: + type: "string" + description: "DocumentTagging: Engine to use for document tagging generation" + enum: + - chatgpt + tags: + description: "DocumentTagging: List of Tags to generate tags for" + items: + type: "string" + AddDocumentOcrRequest: + type: object + example: + parseTypes: + - TEXT + - FORMS + - TABLES + properties: + parseTypes: + type: "array" + description: "OCR Parse types - TEXT, FORMS, TABLES" + items: + type: "string" + addPdfDetectedCharactersAsText: + type: "boolean" + description: "Rewrite PDF document, converting any Image text to searchable text" + SetDocumentOcrRequest: + required: + - "content" + type: "object" + properties: + contentType: + type: "string" + description: "Document Content-Type" + isBase64: + type: "boolean" + description: "Is the content Base64-encoded?" + content: + type: "string" + description: "Document content" + GetVersionRequest: + type: object + properties: + version: + type: string + description: "FormKiQ version" + type: + type: string + description: "FormKiQ type" + modules: + type: array + description: "List of installed modules" + items: + type: "string" + SetConfigRequest: + type: "object" + properties: + chatGptApiKey: + type: "string" + description: "ChatGPT Api Key" + maxContentLengthBytes: + type: "string" + description: "Set Maximum Document Content Length in Bytes" + maxDocuments: + type: "string" + description: "Set Maximum number of Documents allowed" + maxWebhooks: + type: "string" + description: "Set Maximum number of Webhooks allowed" + SetConfigResponse: + type: object + properties: + message: + type: "string" + description: "Result message" + AddApiKeyRequest: + type: object + properties: + name: + type: "string" + description: "Name of API Key" + AddApiKeyResponse: + type: object + properties: + message: + type: "string" + description: "Result message" + DeleteApiKeyResponse: + type: object + properties: + message: + type: "string" + description: "Result message" + GetApiKeysResponse: + type: object + properties: + apiKeys: + $ref: "#/components/schemas/ApiKeys" + ApiKeys: + type: "array" + description: "List of ApiKeys" + items: + $ref: "#/components/schemas/ApiKey" + ApiKey: + type: "object" + properties: + name: + type: "string" + description: "Name of API Key" + apiKey: + type: "string" + description: "API Key value" + userId: + type: "string" + siteIds: + type: "array" + items: + type: "string" + insertedDate: + type: "string" + format: "date-time" + description: "Inserted Timestamp" + GetConfigurationResponse: + type: object + properties: + chatGptApiKey: + type: "string" + description: "ChatGPT API Key" + maxContentLengthBytes: + type: "string" + description: "Set Maximum Document Content Length in Bytes" + maxDocuments: + type: "string" + description: "Set Maximum number of Documents allowed" + maxWebhooks: + type: "string" + description: "Set Maximum number of Webhooks allowed" + GetSitesRequest: + type: object + properties: + sites: + $ref: "#/components/schemas/Sites" + Sites: + type: "array" + description: "List of sites" + items: + $ref: "#/components/schemas/Site" + Site: + type: "object" + properties: + siteId: + type: "string" + description: "Site Identifier" + permission: + type: "string" + description: "SiteId permission level" + enum: + - READ_WRITE + - READ_ONLY + uploadEmail: + type: "string" + description: "SiteId document upload email address" + TagSchemaPostResponse: + type: "object" + properties: + tagSchemaId: + type: "string" + format: "uuid" + description: "Tag Schema Identifier" + ErrorsResponse: + type: object + properties: + errors: + $ref: "#/components/schemas/ErrorsList" + ErrorsList: + type: "array" + description: "List of errors" + items: + $ref: "#/components/schemas/Error" + Error: + type: "object" + properties: + key: + type: "string" + description: "Error Key" + error: + type: "string" + description: "Error Message" + GetTagSchemasRequest: + type: "object" + properties: + schemas: + $ref: "#/components/schemas/TagSchemas" + next: + type: "string" + previous: + type: "string" + TagSchemas: + type: "array" + description: "List of Tag Schemas" + items: + $ref: "#/components/schemas/TagSchemaSummary" + AddTagSchemaRequest: + type: "object" + properties: + name: + type: "string" + tags: + $ref: "#/components/schemas/AddTagSchemaTags" + AddTagSchemaTags: + type: "object" + properties: + compositeKeys: + type: "array" + description: "List of Composite Keys" + items: + $ref: "#/components/schemas/TagSchemaCompositeKey" + required: + type: "array" + description: "List of Required Tags" + items: + $ref: "#/components/schemas/TagSchemaRequired" + optional: + type: "array" + description: "List of Optional Tags" + items: + $ref: "#/components/schemas/TagSchemaOptional" + allowAdditionalTags: + type: "boolean" + default: true + TagSchemaSummary: + type: "object" + properties: + tagSchemaId: + type: "string" + name: + type: "string" + userId: + type: "string" + insertedDate: + type: "string" + format: "date-time" + description: "Inserted Timestamp" + GetTagSchemaRequest: + type: "object" + properties: + tagSchemaId: + type: "string" + name: + type: "string" + userId: + type: "string" + insertedDate: + type: "string" + format: "date-time" + description: "Inserted Timestamp" + tags: + type: "object" + properties: + compositeKeys: + type: "array" + description: "List of Composite Keys" + items: + $ref: "#/components/schemas/TagSchemaCompositeKey" + required: + type: "array" + description: "List of Required Tags" + items: + $ref: "#/components/schemas/TagSchemaRequired" + optional: + type: "array" + description: "List of Optional Tags" + items: + $ref: "#/components/schemas/TagSchemaOptional" + allowAdditionalTags: + type: "boolean" + default: true + TagSchemaCompositeKey: + type: "object" + properties: + key: + type: "array" + items: + type: "string" + TagSchemaRequired: + type: "object" + properties: + key: + type: "string" + defaultValues: + type: "array" + description: "Default values" + items: + type: "string" + allowedValues: + type: "array" + description: "Only valid values" + items: + type: "string" + TagSchemaOptional: + type: "object" + properties: + key: + type: "string" + defaultValues: + type: "array" + description: "Default values" + items: + type: "string" + allowedValues: + type: "array" + description: "Only valid values" + items: + type: "string" + OnlyOfficeDocumentNewRequest: + description: "ONLYOFFICE New Document Request" + properties: + extension: + type: "string" + enum: + - DOCX + - XLSX + - PPTX + OnlyOfficeDocumentEditRequest: + description: "ONLYOFFICE Edit Document Request" + OnlyOfficeDocumentResponse: + type: "object" + properties: + config: + type: "object" + properties: + onlyOfficeUrl: + description: "URL of the ONLYOFFICE application" + type: "string" + token: + description: "ONLYOFFICE security token" + type: "string" + documentType: + description: "Type of document (https://api.onlyoffice.com/editors/config/)" + type: "string" + editorConfig: + type: "object" + properties: + callbackUrl: + type: "string" + document: + type: "object" + properties: + url: + description: "Defines the absolute URL where the source viewed or edited document is stored" + type: "string" + title: + description: "Defines the desired file name for the viewed or edited document" + type: "string" + key: + description: "Defines the unique document identifier used by the service to recognize the document" + type: "string" + fileType: + description: "Defines the type of the file for the source viewed or edited document" + type: "string" + OnlyOfficeDocumentSaveResponse: + type: "object" + properties: + message: + type: "string" + EsignatureDocusignRequest: + type: "object" + properties: + emailSubject: + type: "string" + description: "Email Subject" + status: + type: "string" + description: "The status of the request" + enum: + - created + - sent + developmentMode: + type: "boolean" + description: "Whether to enable developer mode" + signers: + $ref: "#/components/schemas/EsignatureDocusignSigners" + carbonCopies: + $ref: "#/components/schemas/EsignatureDocusignCarbonCopies" + recipientTabs: + $ref: "#/components/schemas/EsignatureDocusignRecipientTabs" + EsignatureDocusignSigners: + type: "array" + description: "List of DocuSign Signers" + items: + $ref: "#/components/schemas/EsignatureDocusignSigner" + EsignatureDocusignSigner: + type: "object" + properties: + name: + type: "string" + description: "Name of Signer" + email: + type: "string" + description: "Email of Signer" + EsignatureDocusignCarbonCopies: + type: "array" + description: "List of DocuSign Carbon Copies" + items: + $ref: "#/components/schemas/EsignatureDocusignCarbonCopy" + EsignatureDocusignCarbonCopy: + type: "object" + properties: + name: + type: "string" + description: "Name of Signer" + email: + type: "string" + description: "Email of Signer" + EsignatureDocusignRecipientTabs: + type: "array" + description: "List of DocuSign Recipient Tabs" + items: + $ref: "#/components/schemas/EsignatureDocusignRecipientTab" + EsignatureDocusignRecipientTab: + type: "object" + properties: + type: + type: "string" + description: "Type of Recipient" + enum: + - signHere + pageNumber: + type: "string" + description: "Document Page Number" + positionX: + type: "string" + description: "Element X Position" + positionY: + type: "string" + description: "Element Y Position" + EsignatureDocusignResponse: + type: "object" + properties: + redirect_url: + type: "string" + description: "Redirect Url to complete DocuSign workflow" + message: + type: "string" + description: "Result message" + EsignatureDocusignConfigResponse: + type: "object" + properties: + configured: + type: "boolean" + description: "Whether DocuSign is configured" + userId: + type: "string" + description: "DocuSign UserId configured" + clientId: + type: "string" + description: "DocuSign Client configured" + EsignatureSetDocusignConfigRequest: + type: "object" + required: + - privateKey + - userId + - clientId + properties: + privateKey: + type: "string" + description: "DocuSign App RSA PRIVATE KEY" + userId: + type: "string" + description: "DocuSign App UserId" + clientId: + type: "string" + description: "DocuSign App Client" + EsignatureSetDocusignConfigResponse: + type: "object" + properties: + message: + type: "string" + description: "Message response" + securitySchemes: + ApiAuthorization: + type: "apiKey" + name: "Authorization" + in: "header" + x-amazon-apigateway-authtype: "Custom API Key" + x-amazon-apigateway-authorizer: + identitySource: $request.header.Authorization + authorizerUri: + Fn::Sub: "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ApiKeyAuthorizer.Arn}/invocations" + authorizerResultTtlInSeconds: 300 + authorizerPayloadFormatVersion: "2.0" + enableSimpleResponses: true + type: request + x-amazon-apigateway-integrations: + lambdaApi200: + uri: + Fn::Sub: "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${DocumentsApiRequests.Arn}/invocations" + responses: + default: + statusCode: "200" + responseParameters: + method.response.header.Access-Control-Allow-Methods: "'*'" + method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key'" + method.response.header.Access-Control-Allow-Origin: "'*'" + passthroughBehavior: "when_no_templates" + httpMethod: "POST" + type: "aws_proxy" + payloadFormatVersion: "1.0" + ocrLambdaApi200: + uri: + Fn::Sub: "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${DocumentsApiRequests.Arn}/invocations" + responses: + default: + statusCode: "200" + responseParameters: + method.response.header.Access-Control-Allow-Methods: "'*'" + method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key'" + method.response.header.Access-Control-Allow-Origin: "'*'" + passthroughBehavior: "when_no_templates" + httpMethod: "POST" + type: "aws_proxy" + payloadFormatVersion: "1.0" + antivirusLambdaApi200: + uri: + Fn::Sub: "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${DocumentsApiRequests.Arn}/invocations" + responses: + default: + statusCode: "200" + responseParameters: + method.response.header.Access-Control-Allow-Methods: "'*'" + method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key'" + method.response.header.Access-Control-Allow-Origin: "'*'" + passthroughBehavior: "when_no_templates" + httpMethod: "POST" + type: "aws_proxy" + payloadFormatVersion: "1.0" + searchLambdaApi200: + uri: + Fn::Sub: "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${DocumentsApiRequests.Arn}/invocations" + responses: + default: + statusCode: "200" + responseParameters: + method.response.header.Access-Control-Allow-Methods: "'*'" + method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key'" + method.response.header.Access-Control-Allow-Origin: "'*'" + passthroughBehavior: "when_no_templates" + httpMethod: "POST" + type: "aws_proxy" + payloadFormatVersion: "1.0" + fulltextLambdaApi200: + uri: + Fn::Sub: "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${DocumentsApiRequests.Arn}/invocations" + responses: + default: + statusCode: "200" + responseParameters: + method.response.header.Access-Control-Allow-Methods: "'*'" + method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key'" + method.response.header.Access-Control-Allow-Origin: "'*'" + passthroughBehavior: "when_no_templates" + httpMethod: "POST" + type: "aws_proxy" + payloadFormatVersion: "1.0" + onlyOfficeLambdaApi200: + uri: + Fn::Sub: "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${DocumentsApiRequests.Arn}/invocations" + responses: + default: + statusCode: "200" + responseParameters: + method.response.header.Access-Control-Allow-Methods: "'*'" + method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key'" + method.response.header.Access-Control-Allow-Origin: "'*'" + passthroughBehavior: "when_no_templates" + httpMethod: "POST" + type: "aws_proxy" + payloadFormatVersion: "1.0" + esignatureLambdaApi200: + uri: + Fn::Sub: "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${DocumentsApiRequests.Arn}/invocations" + responses: + default: + statusCode: "200" + responseParameters: + method.response.header.Access-Control-Allow-Methods: "'*'" + method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key'" + method.response.header.Access-Control-Allow-Origin: "'*'" + passthroughBehavior: "when_no_templates" + httpMethod: "POST" + type: "aws_proxy" + payloadFormatVersion: "1.0" + lambdaApi201: + uri: + Fn::Sub: "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${DocumentsApiRequests.Arn}/invocations" + responses: + default: + statusCode: "201" + responseParameters: + method.response.header.Access-Control-Allow-Headers: '''Content-Type,X-Amz-Date,Authorization,X-Api-Key''' + method.response.header.Access-Control-Allow-Methods: '''*''' + method.response.header.Access-Control-Allow-Origin: '''*''' + passthroughBehavior: WHEN_NO_TEMPLATES + httpMethod: POST + type: aws_proxy + payloadFormatVersion: "1.0" + x-amazon-apigateway-gateway-responses: + UNAUTHORIZED: + statusCode: 401 + responseParameters: + gatewayresponse.header.Access-Control-Allow-Methods: "'*'" + gatewayresponse.header.Access-Control-Allow-Origin: "'*'" + gatewayresponse.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key'" + x-amazon-apigateway-cors: + allowOrigins: + - "*" + maxAge: 3600 + allowMethods: + - "*" + allowHeaders: + - "*" + tags: + - name: Application + x-amazon-apigateway-tag-value: "FormKiQ" + - name: AppEnvironment + x-amazon-apigateway-tag-value: + Fn::Sub: "${AppEnvironment}" diff --git a/lambda-api/src/main/resources/cloudformation/api-iam.yaml b/lambda-api/src/main/resources/cloudformation/api-iam.yaml index d7d9d8935..e18ccfca4 100644 --- a/lambda-api/src/main/resources/cloudformation/api-iam.yaml +++ b/lambda-api/src/main/resources/cloudformation/api-iam.yaml @@ -1,5 +1,6 @@ +#@ load("@ytt:data", "data") +#@ load("@ytt:assert", "assert") #@ load("@ytt:overlay", "overlay") - #@overlay/match by=overlay.all --- Resources: @@ -18,21 +19,21 @@ Resources: url: https://formkiq.com email: support@formkiq.com x-logo: - url: https://docs.formkiq.com/docs/1.9.0/_images/formkiq-logo.png + url: https://docs.formkiq.com/docs/latest/_images/formkiq-logo.png backgroundColor: "#FFFFFF" altText: "FormKiQ Logo" license: name: Apache 2.0 url: https://www.apache.org/licenses/LICENSE-2.0.html description: "FormKiQ IAM API" - version: "1.10.0" + version: #@ data.values.version or assert.fail("missing version") paths: /version: get: operationId: GetVersion description: Return the version of FormKiQ tags: - - Miscellaneous + - System Management responses: '200': description: "200 OK" @@ -51,7 +52,7 @@ Resources: schema: "$ref": "#/components/schemas/GetVersionRequest" security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" /sites: @@ -59,7 +60,7 @@ Resources: operationId: GetSites description: Returns the list of sites that the user has access to tags: - - Miscellaneous + - System Management responses: '200': description: "200 OK" @@ -78,7 +79,165 @@ Resources: schema: "$ref": "#/components/schemas/GetSitesRequest" security: - - sigv4: [] + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + /configuration: + get: + operationId: GetConfigs + description: Returns the list of sites that the user has access to + tags: + - System Management + parameters: + - $ref: '#/components/parameters/siteIdParam' + responses: + '200': + description: "200 OK" + headers: + Access-Control-Allow-Origin: + schema: + type: "string" + Access-Control-Allow-Methods: + schema: + type: "string" + Access-Control-Allow-Headers: + schema: + type: "string" + content: + application/json: + schema: + "$ref": "#/components/schemas/GetConfigurationResponse" + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + patch: + operationId: UpdateConfig + description: Update the System Management configuration + tags: + - System Management + parameters: + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SetConfigRequest' + examples: + Config: + value: + chatGptApiKey: "ABC" + maxContentLengthBytes: "1000000" + maxDocuments: "1000" + maxWebhooks: "10" + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/SetConfigResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + /configuration/apiKeys: + get: + operationId: GetApiKeys + description: Returns the list of ApiKeys + tags: + - System Management + parameters: + - $ref: '#/components/parameters/siteIdParam' + responses: + '200': + description: "200 OK" + headers: + Access-Control-Allow-Origin: + schema: + type: "string" + Access-Control-Allow-Methods: + schema: + type: "string" + Access-Control-Allow-Headers: + schema: + type: "string" + content: + application/json: + schema: + "$ref": "#/components/schemas/GetApiKeysResponse" + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + post: + operationId: Ã…ddApiKey + description: Adds a new API Key + tags: + - System Management + parameters: + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AddApiKeyRequest' + examples: + AddApiKey: + value: + name: My API Key + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/AddApiKeyResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + /configuration/apiKeys/{apiKey}: + delete: + operationId: DeleteApiKey + description: Adds a new API Key + tags: + - System Management + parameters: + - $ref: '#/components/parameters/apiKeyParam' + - $ref: '#/components/parameters/siteIdParam' + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/DeleteApiKeyResponse' + security: + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" /tagSchemas: @@ -118,7 +277,7 @@ Resources: schema: "$ref": "#/components/schemas/GetTagSchemasRequest" security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" post: @@ -162,7 +321,7 @@ Resources: schema: $ref: '#/components/schemas/ErrorsResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi201" /tagSchemas/{tagSchemaId}: @@ -189,7 +348,7 @@ Resources: schema: $ref: '#/components/schemas/GetTagSchemaRequest' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" delete: @@ -204,7 +363,7 @@ Resources: '200': $ref: '#/components/responses/200Cors' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" /documents: @@ -254,7 +413,7 @@ Resources: schema: "$ref": "#/components/schemas/GetDocumentsResponse" security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" post: @@ -324,7 +483,7 @@ Resources: schema: $ref: '#/components/schemas/AddDocumentResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi201" /documents/{documentId}: @@ -351,7 +510,7 @@ Resources: schema: $ref: '#/components/schemas/GetDocumentResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" patch: @@ -398,7 +557,7 @@ Resources: schema: $ref: '#/components/schemas/AddDocumentResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" delete: @@ -413,7 +572,7 @@ Resources: '200': $ref: '#/components/responses/200Cors' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" /documents/{documentId}/versions: @@ -445,7 +604,7 @@ Resources: schema: $ref: '#/components/schemas/GetDocumentVersionsResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" put: @@ -483,7 +642,7 @@ Resources: schema: $ref: '#/components/schemas/ValidationErrorsResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" /documents/{documentId}/versions/{versionKey}: @@ -499,7 +658,7 @@ Resources: '200': $ref: '#/components/responses/200Cors' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" /documents/{documentId}/content: @@ -529,7 +688,7 @@ Resources: schema: $ref: '#/components/schemas/GetDocumentContentResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" /documents/{documentId}/tags: @@ -567,7 +726,7 @@ Resources: schema: $ref: '#/components/schemas/GetDocumentTagsResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" post: @@ -600,7 +759,7 @@ Resources: '201': $ref: '#/components/responses/200Cors' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi201" /documents/{documentId}/tags#: @@ -622,7 +781,7 @@ Resources: '201': $ref: '#/components/responses/200Cors' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi201" /documents/{documentId}/tags/{tagKey}: @@ -650,7 +809,7 @@ Resources: schema: $ref: '#/components/schemas/GetDocumentTagResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" put: @@ -672,7 +831,7 @@ Resources: '200': $ref: '#/components/responses/200Cors' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" delete: @@ -688,7 +847,7 @@ Resources: '200': $ref: '#/components/responses/200Cors' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" /documents/{documentId}/tags/{tagKey}/{tagValue}: @@ -706,7 +865,7 @@ Resources: '200': $ref: '#/components/responses/200Cors' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" /documents/{documentId}/url: @@ -741,7 +900,7 @@ Resources: schema: $ref: '#/components/schemas/GetDocumentUrlRequest' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" /documents/upload: @@ -774,7 +933,7 @@ Resources: schema: $ref: '#/components/schemas/GetDocumentUrlRequest' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" post: @@ -821,7 +980,7 @@ Resources: schema: $ref: '#/components/schemas/AddDocumentResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi201" /documents/{documentId}/upload: @@ -850,7 +1009,7 @@ Resources: schema: $ref: '#/components/schemas/GetDocumentUrlRequest' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" /search: @@ -918,7 +1077,7 @@ Resources: schema: $ref: '#/components/schemas/DocumentSearchResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/searchLambdaApi200" /searchFulltext: @@ -951,7 +1110,7 @@ Resources: schema: $ref: '#/components/schemas/DocumentFulltextResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200" /queryFulltext: @@ -983,7 +1142,7 @@ Resources: schema: $ref: '#/components/schemas/QueryFulltextResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200" /documents/{documentId}/actions: @@ -1010,7 +1169,7 @@ Resources: schema: $ref: '#/components/schemas/GetDocumentActionsResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" post: @@ -1042,7 +1201,7 @@ Resources: schema: $ref: '#/components/schemas/AddDocumentActionsResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" /documents/{documentId}/ocr: @@ -1071,7 +1230,7 @@ Resources: schema: $ref: '#/components/schemas/GetDocumentOcrResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/ocrLambdaApi200" post: @@ -1103,7 +1262,7 @@ Resources: schema: $ref: '#/components/schemas/AddDocumentOcrResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/ocrLambdaApi200" put: @@ -1135,7 +1294,7 @@ Resources: schema: $ref: '#/components/schemas/AddDocumentOcrResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/ocrLambdaApi200" delete: @@ -1150,7 +1309,7 @@ Resources: '200': $ref: '#/components/responses/200Cors' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/ocrLambdaApi200" /documents/{documentId}/antivirus: @@ -1183,7 +1342,7 @@ Resources: schema: $ref: '#/components/schemas/SetAntivirusResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/antivirusLambdaApi200" /documents/{documentId}/fulltext: @@ -1210,7 +1369,7 @@ Resources: schema: $ref: '#/components/schemas/GetDocumentFulltextResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200" delete: @@ -1225,7 +1384,7 @@ Resources: '200': $ref: '#/components/responses/200Cors' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200" put: @@ -1257,7 +1416,7 @@ Resources: schema: $ref: '#/components/schemas/SetDocumentFulltextResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200" patch: @@ -1289,7 +1448,7 @@ Resources: schema: $ref: '#/components/schemas/SetDocumentFulltextResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200" /documents/{documentId}/fulltext/tags/{tagKey}: @@ -1306,7 +1465,7 @@ Resources: '200': $ref: '#/components/responses/200Cors' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200" /documents/{documentId}/fulltext/tags/{tagKey}/{tagValue}: @@ -1324,7 +1483,7 @@ Resources: '200': $ref: '#/components/responses/200Cors' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200" /documents/{documentId}/syncs: @@ -1352,7 +1511,7 @@ Resources: schema: $ref: '#/components/schemas/GetDocumentSyncResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" /public/documents: @@ -1446,7 +1605,7 @@ Resources: schema: $ref: '#/components/schemas/DocumentId' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" /webhooks: @@ -1475,7 +1634,7 @@ Resources: schema: "$ref": "#/components/schemas/GetWebhooksResponse" security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" post: @@ -1506,7 +1665,7 @@ Resources: schema: $ref: '#/components/schemas/AddWebhookResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi201" /webhooks/{webhookId}: @@ -1536,7 +1695,7 @@ Resources: schema: "$ref": "#/components/schemas/GetWebhookResponse" security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" delete: @@ -1551,7 +1710,7 @@ Resources: '200': $ref: '#/components/responses/200Cors' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" patch: @@ -1572,7 +1731,7 @@ Resources: '200': $ref: '#/components/responses/200Cors' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" /webhooks/{webhookId}/tags: @@ -1599,7 +1758,7 @@ Resources: schema: $ref: '#/components/schemas/GetWebhookTagsResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" post: @@ -1628,7 +1787,7 @@ Resources: '201': $ref: '#/components/responses/200Cors' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi201" /onlyoffice/{documentId}/edit: @@ -1661,7 +1820,7 @@ Resources: schema: $ref: '#/components/schemas/OnlyOfficeDocumentResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/onlyOfficeLambdaApi200" /onlyoffice/new: @@ -1693,7 +1852,7 @@ Resources: schema: $ref: '#/components/schemas/OnlyOfficeDocumentResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/onlyOfficeLambdaApi200" /onlyoffice/{documentId}/save: @@ -1757,7 +1916,7 @@ Resources: schema: $ref: '#/components/schemas/ValidationErrorsResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" /indices/{indexType}/{indexKey}: @@ -1787,7 +1946,7 @@ Resources: schema: $ref: '#/components/schemas/ValidationErrorsResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" /indices/search: @@ -1837,7 +1996,7 @@ Resources: schema: $ref: '#/components/schemas/ValidationErrorsResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" /esignature/docusign/{documentId}: @@ -1876,7 +2035,7 @@ Resources: schema: $ref: '#/components/schemas/ValidationErrorsResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/esignatureLambdaApi200" /esignature/docusign/config: @@ -1902,7 +2061,7 @@ Resources: schema: $ref: '#/components/schemas/EsignatureDocusignConfigResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/esignatureLambdaApi200" put: @@ -1933,7 +2092,7 @@ Resources: schema: $ref: '#/components/schemas/EsignatureSetDocusignConfigResponse' security: - - sigv4: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/esignatureLambdaApi200" /esignature/docusign/events: @@ -2037,6 +2196,13 @@ Resources: schema: type: string default: "10" + apiKeyParam: + name: apiKey + in: path + description: API Key + required: true + schema: + type: string documentIdParam: name: documentId in: path @@ -2741,6 +2907,9 @@ Resources: type: "string" format: "uuid" description: "Document Identifier" + contentLength: + type: "integer" + description: "Document size" createdBy: type: "string" description: "User who added document" @@ -2769,6 +2938,9 @@ Resources: createdBy: type: "string" description: "User who added document" + contentLength: + type: "integer" + description: "Document size" tags: type: "object" metadata: @@ -3156,6 +3328,7 @@ Resources: - FULLTEXT - ANTIVIRUS - WEBHOOK + - DOCUMENTTAGGING parameters: $ref: "#/components/schemas/AddActionParameters" AddActionParameters: @@ -3173,6 +3346,15 @@ Resources: characterMax: type: "string" description: "Fulltext: Maximum number of characters (-1 unlimited, Typesense defaults to 2048 characters)" + engine: + type: "string" + description: "DocumentTagging: Engine to use for document tagging generation" + enum: + - chatgpt + tags: + description: "DocumentTagging: List of Tags to generate tags for" + items: + type: "string" AddDocumentOcrRequest: type: object example: @@ -3217,6 +3399,89 @@ Resources: description: "List of installed modules" items: type: "string" + SetConfigRequest: + type: "object" + properties: + chatGptApiKey: + type: "string" + description: "ChatGPT Api Key" + maxContentLengthBytes: + type: "string" + description: "Set Maximum Document Content Length in Bytes" + maxDocuments: + type: "string" + description: "Set Maximum number of Documents allowed" + maxWebhooks: + type: "string" + description: "Set Maximum number of Webhooks allowed" + SetConfigResponse: + type: object + properties: + message: + type: "string" + description: "Result message" + AddApiKeyRequest: + type: object + properties: + name: + type: "string" + description: "Name of API Key" + AddApiKeyResponse: + type: object + properties: + message: + type: "string" + description: "Result message" + DeleteApiKeyResponse: + type: object + properties: + message: + type: "string" + description: "Result message" + GetApiKeysResponse: + type: object + properties: + apiKeys: + $ref: "#/components/schemas/ApiKeys" + ApiKeys: + type: "array" + description: "List of ApiKeys" + items: + $ref: "#/components/schemas/ApiKey" + ApiKey: + type: "object" + properties: + name: + type: "string" + description: "Name of API Key" + apiKey: + type: "string" + description: "API Key value" + userId: + type: "string" + siteIds: + type: "array" + items: + type: "string" + insertedDate: + type: "string" + format: "date-time" + description: "Inserted Timestamp" + GetConfigurationResponse: + type: object + properties: + chatGptApiKey: + type: "string" + description: "ChatGPT API Key" + maxContentLengthBytes: + type: "string" + description: "Set Maximum Document Content Length in Bytes" + maxDocuments: + type: "string" + description: "Set Maximum number of Documents allowed" + maxWebhooks: + type: "string" + description: "Set Maximum number of Webhooks allowed" GetSitesRequest: type: object properties: @@ -3560,18 +3825,7 @@ Resources: type: "string" description: "Message response" securitySchemes: - AuthorizationCognito: - type: oauth2 - flows: {} - x-amazon-apigateway-authorizer: - type: jwt - jwtConfiguration: - issuer: - Fn::Sub: https://cognito-idp.${AWS::Region}.amazonaws.com/${CognitoUserPool} - audience: - - Fn::Sub: "${CognitoUserPoolClient}" - identitySource: "$request.header.Authorization" - sigv4: + ApiAuthorization: type: "apiKey" name: "Authorization" in: "header" diff --git a/lambda-api/src/main/resources/cloudformation/api-key-variables.yaml b/lambda-api/src/main/resources/cloudformation/api-key-variables.yaml new file mode 100644 index 000000000..eed25154a --- /dev/null +++ b/lambda-api/src/main/resources/cloudformation/api-key-variables.yaml @@ -0,0 +1,11 @@ +#@ load("@ytt:overlay", "overlay") + +#@overlay/match by=overlay.all +--- +Resources: + DocumentsKeyApi: + Properties: + Body: + info: + #@overlay/replace + title: "FormKiQ KEY API" \ No newline at end of file diff --git a/lambda-api/src/main/resources/cloudformation/api.yaml b/lambda-api/src/main/resources/cloudformation/api.yaml index cd2ca5b65..73c4bb7d1 100644 --- a/lambda-api/src/main/resources/cloudformation/api.yaml +++ b/lambda-api/src/main/resources/cloudformation/api.yaml @@ -1,5 +1,6 @@ +#@ load("@ytt:data", "data") +#@ load("@ytt:assert", "assert") #@ load("@ytt:overlay", "overlay") - #@overlay/match by=overlay.all --- Resources: @@ -18,21 +19,21 @@ Resources: url: https://formkiq.com email: support@formkiq.com x-logo: - url: https://docs.formkiq.com/docs/1.9.0/_images/formkiq-logo.png + url: https://docs.formkiq.com/docs/latest/_images/formkiq-logo.png backgroundColor: "#FFFFFF" altText: "FormKiQ Logo" license: name: Apache 2.0 url: https://www.apache.org/licenses/LICENSE-2.0.html description: "FormKiQ HTTP API" - version: "1.10.0" + version: #@ data.values.version or assert.fail("missing version") paths: /version: get: operationId: GetVersion description: Return the version of FormKiQ tags: - - Miscellaneous + - System Management responses: '200': description: "200 OK" @@ -51,7 +52,7 @@ Resources: schema: "$ref": "#/components/schemas/GetVersionRequest" security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" /sites: @@ -59,7 +60,7 @@ Resources: operationId: GetSites description: Returns the list of sites that the user has access to tags: - - Miscellaneous + - System Management responses: '200': description: "200 OK" @@ -78,7 +79,165 @@ Resources: schema: "$ref": "#/components/schemas/GetSitesRequest" security: - - AuthorizationCognito: [] + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + /configuration: + get: + operationId: GetConfigs + description: Returns the list of sites that the user has access to + tags: + - System Management + parameters: + - $ref: '#/components/parameters/siteIdParam' + responses: + '200': + description: "200 OK" + headers: + Access-Control-Allow-Origin: + schema: + type: "string" + Access-Control-Allow-Methods: + schema: + type: "string" + Access-Control-Allow-Headers: + schema: + type: "string" + content: + application/json: + schema: + "$ref": "#/components/schemas/GetConfigurationResponse" + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + patch: + operationId: UpdateConfig + description: Update the System Management configuration + tags: + - System Management + parameters: + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SetConfigRequest' + examples: + Config: + value: + chatGptApiKey: "ABC" + maxContentLengthBytes: "1000000" + maxDocuments: "1000" + maxWebhooks: "10" + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/SetConfigResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + /configuration/apiKeys: + get: + operationId: GetApiKeys + description: Returns the list of ApiKeys + tags: + - System Management + parameters: + - $ref: '#/components/parameters/siteIdParam' + responses: + '200': + description: "200 OK" + headers: + Access-Control-Allow-Origin: + schema: + type: "string" + Access-Control-Allow-Methods: + schema: + type: "string" + Access-Control-Allow-Headers: + schema: + type: "string" + content: + application/json: + schema: + "$ref": "#/components/schemas/GetApiKeysResponse" + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + post: + operationId: Ã…ddApiKey + description: Adds a new API Key + tags: + - System Management + parameters: + - $ref: '#/components/parameters/siteIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AddApiKeyRequest' + examples: + AddApiKey: + value: + name: My API Key + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/AddApiKeyResponse' + security: + - ApiAuthorization: [] + x-amazon-apigateway-integration: + $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" + /configuration/apiKeys/{apiKey}: + delete: + operationId: DeleteApiKey + description: Adds a new API Key + tags: + - System Management + parameters: + - $ref: '#/components/parameters/apiKeyParam' + - $ref: '#/components/parameters/siteIdParam' + responses: + '200': + description: 200 OK + headers: + Access-Control-Allow-Origin: + $ref: '#/components/headers/AccessControlAllowOrigin' + Access-Control-Allow-Methods: + $ref: '#/components/headers/AccessControlAllowMethods' + Access-Control-Allow-Headers: + $ref: '#/components/headers/AccessControlAllowHeaders' + content: + application/json: + schema: + $ref: '#/components/schemas/DeleteApiKeyResponse' + security: + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" /tagSchemas: @@ -118,7 +277,7 @@ Resources: schema: "$ref": "#/components/schemas/GetTagSchemasRequest" security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" post: @@ -162,7 +321,7 @@ Resources: schema: $ref: '#/components/schemas/ErrorsResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi201" /tagSchemas/{tagSchemaId}: @@ -189,7 +348,7 @@ Resources: schema: $ref: '#/components/schemas/GetTagSchemaRequest' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" delete: @@ -204,7 +363,7 @@ Resources: '200': $ref: '#/components/responses/200Cors' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" /documents: @@ -254,7 +413,7 @@ Resources: schema: "$ref": "#/components/schemas/GetDocumentsResponse" security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" post: @@ -324,7 +483,7 @@ Resources: schema: $ref: '#/components/schemas/AddDocumentResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi201" /documents/{documentId}: @@ -351,7 +510,7 @@ Resources: schema: $ref: '#/components/schemas/GetDocumentResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" patch: @@ -398,7 +557,7 @@ Resources: schema: $ref: '#/components/schemas/AddDocumentResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" delete: @@ -413,7 +572,7 @@ Resources: '200': $ref: '#/components/responses/200Cors' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" /documents/{documentId}/versions: @@ -445,7 +604,7 @@ Resources: schema: $ref: '#/components/schemas/GetDocumentVersionsResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" put: @@ -483,7 +642,7 @@ Resources: schema: $ref: '#/components/schemas/ValidationErrorsResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" /documents/{documentId}/versions/{versionKey}: @@ -499,7 +658,7 @@ Resources: '200': $ref: '#/components/responses/200Cors' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" /documents/{documentId}/content: @@ -529,7 +688,7 @@ Resources: schema: $ref: '#/components/schemas/GetDocumentContentResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" /documents/{documentId}/tags: @@ -567,7 +726,7 @@ Resources: schema: $ref: '#/components/schemas/GetDocumentTagsResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" post: @@ -600,7 +759,7 @@ Resources: '201': $ref: '#/components/responses/200Cors' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi201" /documents/{documentId}/tags#: @@ -622,7 +781,7 @@ Resources: '201': $ref: '#/components/responses/200Cors' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi201" /documents/{documentId}/tags/{tagKey}: @@ -650,7 +809,7 @@ Resources: schema: $ref: '#/components/schemas/GetDocumentTagResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" put: @@ -672,7 +831,7 @@ Resources: '200': $ref: '#/components/responses/200Cors' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" delete: @@ -688,7 +847,7 @@ Resources: '200': $ref: '#/components/responses/200Cors' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" /documents/{documentId}/tags/{tagKey}/{tagValue}: @@ -706,7 +865,7 @@ Resources: '200': $ref: '#/components/responses/200Cors' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" /documents/{documentId}/url: @@ -741,7 +900,7 @@ Resources: schema: $ref: '#/components/schemas/GetDocumentUrlRequest' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" /documents/upload: @@ -774,7 +933,7 @@ Resources: schema: $ref: '#/components/schemas/GetDocumentUrlRequest' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" post: @@ -821,7 +980,7 @@ Resources: schema: $ref: '#/components/schemas/AddDocumentResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi201" /documents/{documentId}/upload: @@ -850,7 +1009,7 @@ Resources: schema: $ref: '#/components/schemas/GetDocumentUrlRequest' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" /search: @@ -918,7 +1077,7 @@ Resources: schema: $ref: '#/components/schemas/DocumentSearchResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/searchLambdaApi200" /searchFulltext: @@ -951,7 +1110,7 @@ Resources: schema: $ref: '#/components/schemas/DocumentFulltextResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200" /queryFulltext: @@ -983,7 +1142,7 @@ Resources: schema: $ref: '#/components/schemas/QueryFulltextResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200" /documents/{documentId}/actions: @@ -1010,7 +1169,7 @@ Resources: schema: $ref: '#/components/schemas/GetDocumentActionsResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" post: @@ -1042,7 +1201,7 @@ Resources: schema: $ref: '#/components/schemas/AddDocumentActionsResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" /documents/{documentId}/ocr: @@ -1071,7 +1230,7 @@ Resources: schema: $ref: '#/components/schemas/GetDocumentOcrResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/ocrLambdaApi200" post: @@ -1103,7 +1262,7 @@ Resources: schema: $ref: '#/components/schemas/AddDocumentOcrResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/ocrLambdaApi200" put: @@ -1135,7 +1294,7 @@ Resources: schema: $ref: '#/components/schemas/AddDocumentOcrResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/ocrLambdaApi200" delete: @@ -1150,7 +1309,7 @@ Resources: '200': $ref: '#/components/responses/200Cors' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/ocrLambdaApi200" /documents/{documentId}/antivirus: @@ -1183,7 +1342,7 @@ Resources: schema: $ref: '#/components/schemas/SetAntivirusResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/antivirusLambdaApi200" /documents/{documentId}/fulltext: @@ -1210,7 +1369,7 @@ Resources: schema: $ref: '#/components/schemas/GetDocumentFulltextResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200" delete: @@ -1225,7 +1384,7 @@ Resources: '200': $ref: '#/components/responses/200Cors' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200" put: @@ -1257,7 +1416,7 @@ Resources: schema: $ref: '#/components/schemas/SetDocumentFulltextResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200" patch: @@ -1289,7 +1448,7 @@ Resources: schema: $ref: '#/components/schemas/SetDocumentFulltextResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200" /documents/{documentId}/fulltext/tags/{tagKey}: @@ -1306,7 +1465,7 @@ Resources: '200': $ref: '#/components/responses/200Cors' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200" /documents/{documentId}/fulltext/tags/{tagKey}/{tagValue}: @@ -1324,7 +1483,7 @@ Resources: '200': $ref: '#/components/responses/200Cors' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/fulltextLambdaApi200" /documents/{documentId}/syncs: @@ -1352,7 +1511,7 @@ Resources: schema: $ref: '#/components/schemas/GetDocumentSyncResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" /public/documents: @@ -1446,7 +1605,7 @@ Resources: schema: $ref: '#/components/schemas/DocumentId' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" /webhooks: @@ -1475,7 +1634,7 @@ Resources: schema: "$ref": "#/components/schemas/GetWebhooksResponse" security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" post: @@ -1506,7 +1665,7 @@ Resources: schema: $ref: '#/components/schemas/AddWebhookResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi201" /webhooks/{webhookId}: @@ -1536,7 +1695,7 @@ Resources: schema: "$ref": "#/components/schemas/GetWebhookResponse" security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" delete: @@ -1551,7 +1710,7 @@ Resources: '200': $ref: '#/components/responses/200Cors' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" patch: @@ -1572,7 +1731,7 @@ Resources: '200': $ref: '#/components/responses/200Cors' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" /webhooks/{webhookId}/tags: @@ -1599,7 +1758,7 @@ Resources: schema: $ref: '#/components/schemas/GetWebhookTagsResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" post: @@ -1628,7 +1787,7 @@ Resources: '201': $ref: '#/components/responses/200Cors' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi201" /onlyoffice/{documentId}/edit: @@ -1661,7 +1820,7 @@ Resources: schema: $ref: '#/components/schemas/OnlyOfficeDocumentResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/onlyOfficeLambdaApi200" /onlyoffice/new: @@ -1693,7 +1852,7 @@ Resources: schema: $ref: '#/components/schemas/OnlyOfficeDocumentResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/onlyOfficeLambdaApi200" /onlyoffice/{documentId}/save: @@ -1757,7 +1916,7 @@ Resources: schema: $ref: '#/components/schemas/ValidationErrorsResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" /indices/{indexType}/{indexKey}: @@ -1787,7 +1946,7 @@ Resources: schema: $ref: '#/components/schemas/ValidationErrorsResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" /indices/search: @@ -1837,7 +1996,7 @@ Resources: schema: $ref: '#/components/schemas/ValidationErrorsResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/lambdaApi200" /esignature/docusign/{documentId}: @@ -1876,7 +2035,7 @@ Resources: schema: $ref: '#/components/schemas/ValidationErrorsResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/esignatureLambdaApi200" /esignature/docusign/config: @@ -1902,7 +2061,7 @@ Resources: schema: $ref: '#/components/schemas/EsignatureDocusignConfigResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/esignatureLambdaApi200" put: @@ -1933,7 +2092,7 @@ Resources: schema: $ref: '#/components/schemas/EsignatureSetDocusignConfigResponse' security: - - AuthorizationCognito: [] + - ApiAuthorization: [] x-amazon-apigateway-integration: $ref: "#/components/x-amazon-apigateway-integrations/esignatureLambdaApi200" /esignature/docusign/events: @@ -2037,6 +2196,13 @@ Resources: schema: type: string default: "10" + apiKeyParam: + name: apiKey + in: path + description: API Key + required: true + schema: + type: string documentIdParam: name: documentId in: path @@ -2741,6 +2907,9 @@ Resources: type: "string" format: "uuid" description: "Document Identifier" + contentLength: + type: "integer" + description: "Document size" createdBy: type: "string" description: "User who added document" @@ -2769,6 +2938,9 @@ Resources: createdBy: type: "string" description: "User who added document" + contentLength: + type: "integer" + description: "Document size" tags: type: "object" metadata: @@ -3156,6 +3328,7 @@ Resources: - FULLTEXT - ANTIVIRUS - WEBHOOK + - DOCUMENTTAGGING parameters: $ref: "#/components/schemas/AddActionParameters" AddActionParameters: @@ -3173,6 +3346,15 @@ Resources: characterMax: type: "string" description: "Fulltext: Maximum number of characters (-1 unlimited, Typesense defaults to 2048 characters)" + engine: + type: "string" + description: "DocumentTagging: Engine to use for document tagging generation" + enum: + - chatgpt + tags: + description: "DocumentTagging: List of Tags to generate tags for" + items: + type: "string" AddDocumentOcrRequest: type: object example: @@ -3217,6 +3399,89 @@ Resources: description: "List of installed modules" items: type: "string" + SetConfigRequest: + type: "object" + properties: + chatGptApiKey: + type: "string" + description: "ChatGPT Api Key" + maxContentLengthBytes: + type: "string" + description: "Set Maximum Document Content Length in Bytes" + maxDocuments: + type: "string" + description: "Set Maximum number of Documents allowed" + maxWebhooks: + type: "string" + description: "Set Maximum number of Webhooks allowed" + SetConfigResponse: + type: object + properties: + message: + type: "string" + description: "Result message" + AddApiKeyRequest: + type: object + properties: + name: + type: "string" + description: "Name of API Key" + AddApiKeyResponse: + type: object + properties: + message: + type: "string" + description: "Result message" + DeleteApiKeyResponse: + type: object + properties: + message: + type: "string" + description: "Result message" + GetApiKeysResponse: + type: object + properties: + apiKeys: + $ref: "#/components/schemas/ApiKeys" + ApiKeys: + type: "array" + description: "List of ApiKeys" + items: + $ref: "#/components/schemas/ApiKey" + ApiKey: + type: "object" + properties: + name: + type: "string" + description: "Name of API Key" + apiKey: + type: "string" + description: "API Key value" + userId: + type: "string" + siteIds: + type: "array" + items: + type: "string" + insertedDate: + type: "string" + format: "date-time" + description: "Inserted Timestamp" + GetConfigurationResponse: + type: object + properties: + chatGptApiKey: + type: "string" + description: "ChatGPT API Key" + maxContentLengthBytes: + type: "string" + description: "Set Maximum Document Content Length in Bytes" + maxDocuments: + type: "string" + description: "Set Maximum number of Documents allowed" + maxWebhooks: + type: "string" + description: "Set Maximum number of Webhooks allowed" GetSitesRequest: type: object properties: @@ -3560,7 +3825,7 @@ Resources: type: "string" description: "Message response" securitySchemes: - AuthorizationCognito: + ApiAuthorization: type: oauth2 flows: {} x-amazon-apigateway-authorizer: diff --git a/lambda-api/src/main/resources/cloudformation/template-apikey.yaml b/lambda-api/src/main/resources/cloudformation/template-apikey.yaml new file mode 100644 index 000000000..7538be631 --- /dev/null +++ b/lambda-api/src/main/resources/cloudformation/template-apikey.yaml @@ -0,0 +1,99 @@ +#@ load("@ytt:overlay", "overlay") +#@ load("@ytt:data", "data") +#@ load("@ytt:assert", "assert") +#@overlay/match by=overlay.all +--- +Resources: + + #@overlay/match missing_ok=True + ApiKeyAuthorizer: + Type: AWS::Serverless::Function + DependsOn: + - ApiKeyAuthorizerRole + Properties: + Handler: com.formkiq.module.lambda.authorizer.apikey.ApiKeyAuthorizerRequestHandler + Description: Lambda function that processes ApiKey Authorizer + Runtime: provided + Timeout: + Fn::Sub: "${LambdaTimeout}" + MemorySize: + Fn::Sub: "${LambdaMemory}" + CodeUri: ./formkiq-module-lambda-authorizer-apikey.zip + Tracing: Active + AutoPublishCodeSha256: #@ data.values.hash or assert.fail("missing version") + Environment: + Variables: + APP_ENVIRONMENT: + Fn::Sub: "${AppEnvironment}" + DEBUG: false + ENABLE_AWS_X_RAY: true + DOCUMENTS_TABLE: + Fn::Sub: "{{resolve:ssm:/formkiq/${AppEnvironment}/dynamodb/DocumentsTableName}}" + FORMKIQ_TYPE: + Ref: FormKiQType + Role: + Fn::GetAtt: + - ApiKeyAuthorizerRole + - Arn + Tags: + Application: + Fn::Sub: "FormKiQ ${FormKiQType}" + AppEnvironment: + Fn::Sub: "${AppEnvironment}" + StackName: + Fn::Sub: "${AWS::StackName}" + + #@overlay/match missing_ok=True + ApiKeyAuthorizerInvokePermission: + Type: "AWS::Lambda::Permission" + DependsOn: + - ApiKeyAuthorizer + Properties: + Action: lambda:InvokeFunction + FunctionName: + Ref: ApiKeyAuthorizer + Principal: apigateway.amazonaws.com + + #@overlay/match missing_ok=True + ApiKeyAuthorizerRole: + Type: AWS::IAM::Role + Properties: + Tags: + - Key: "Application" + Value: + Fn::Sub: "FormKiQ ${FormKiQType}" + - Key: "AppEnvironment" + Value: + Fn::Sub: "${AppEnvironment}" + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Principal: + Service: + - lambda.amazonaws.com + Action: + - sts:AssumeRole + Path: / + ManagedPolicyArns: + - arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess + Policies: + - + PolicyName: apikeylambdarole + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - logs:CreateLogGroup + - logs:CreateLogStream + - logs:PutLogEvents + Resource: "*" + - Effect: Allow + Action: + - dynamodb:BatchGetItem + - dynamodb:BatchWriteItem + - dynamodb:Get* + - dynamodb:Query + Resource: + - Fn::Sub: "arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/{{resolve:ssm:/formkiq/${AppEnvironment}/dynamodb/DocumentsTableName}}" \ No newline at end of file diff --git a/lambda-api/src/main/resources/cloudformation/template-ocr.yaml b/lambda-api/src/main/resources/cloudformation/template-ocr.yaml new file mode 100644 index 000000000..f3db16ae1 --- /dev/null +++ b/lambda-api/src/main/resources/cloudformation/template-ocr.yaml @@ -0,0 +1,177 @@ +#@ load("@ytt:overlay", "overlay") +#@ load("@ytt:data", "data") +#@ load("@ytt:assert", "assert") +#@overlay/match by=overlay.all +--- +Resources: + + #@overlay/match missing_ok=True + OcrBucket: + Type: AWS::S3::Bucket + Properties: + BucketName: + Fn::Sub: "formkiq-${FormKiQType}-${AppEnvironment}-ocr-${AWS::AccountId}" + BucketEncryption: + ServerSideEncryptionConfiguration: + - ServerSideEncryptionByDefault: + SSEAlgorithm: AES256 + LifecycleConfiguration: + Rules: + - Id: ExpiryRule + Prefix: tempfiles/ + Status: Enabled + ExpirationInDays: 1 + VersioningConfiguration: + Status: Enabled + PublicAccessBlockConfiguration: + BlockPublicAcls: true + IgnorePublicAcls: true + BlockPublicPolicy: true + RestrictPublicBuckets: true + Tags: + - Key: "Application" + Value: + Fn::Sub: "FormKiQ ${FormKiQType}" + - Key: "AppEnvironment" + Value: + Fn::Sub: "${AppEnvironment}" + - Key: "StackName" + Value: + Fn::Sub: "${AWS::StackName}" + + #@overlay/match missing_ok=True + OcrBucketParameter: + Type: AWS::SSM::Parameter + Properties: + Description: "OCR S3 Bucket Name" + Name: + Fn::Sub: "/formkiq/${AppEnvironment}/s3/OcrBucket" + Type: String + Value: + Ref: OcrBucket + Tags: + Application: + Fn::Sub: "FormKiQ ${FormKiQType}" + AppEnvironment: + Fn::Sub: "${AppEnvironment}" + StackName: + Fn::Sub: "${AWS::StackName}" + + #@overlay/match missing_ok=True + OcrQueue: + Type: AWS::SQS::Queue + Properties: + VisibilityTimeout: 700 + ReceiveMessageWaitTimeSeconds: 20 + Tags: + - Key: "Application" + Value: + Fn::Sub: "FormKiQ ${FormKiQType}" + - Key: "AppEnvironment" + Value: + Fn::Sub: "${AppEnvironment}" + - Key: "StackName" + Value: + Fn::Sub: "${AWS::StackName}" + + #@overlay/match missing_ok=True + OcrQueuePolicy: + Type: AWS::SQS::QueuePolicy + DependsOn: OcrQueue + Properties: + PolicyDocument: + Version: '2012-10-17' + Id: QueuePolicy + Statement: + - Sid: LambdaSendMessagesToQueue + Effect: Allow + Principal: + Service: + - lambda.amazonaws.com + Action: sqs:SendMessage + Resource: + Fn::GetAtt: + - OcrQueue + - Arn + Queues: + - Ref: OcrQueue + + #@overlay/match missing_ok=True + OcrQueueParameter: + Type: AWS::SSM::Parameter + Properties: + Description: "Ocr SQS Queue" + Name: + Fn::Sub: "/formkiq/${AppEnvironment}/sqs/OcrQueue" + Type: String + Value: + Ref: OcrQueue + Tags: + Application: + Fn::Sub: "FormKiQ ${FormKiQType}" + AppEnvironment: + Fn::Sub: "${AppEnvironment}" + StackName: + Fn::Sub: "${AWS::StackName}" + + #@overlay/match missing_ok=True + OcrQueueUrlParameter: + Type: AWS::SSM::Parameter + Properties: + Description: "Ocr SQS Queue Url" + Name: + Fn::Sub: "/formkiq/${AppEnvironment}/sqs/OcrQueueUrl" + Type: String + Value: + Fn::GetAtt: + - OcrQueue + - QueueUrl + Tags: + Application: + Fn::Sub: "FormKiQ ${FormKiQType}" + AppEnvironment: + Fn::Sub: "${AppEnvironment}" + StackName: + Fn::Sub: "${AWS::StackName}" + + #@overlay/match missing_ok=True + OcrQueueArnParameter: + Type: AWS::SSM::Parameter + Properties: + Description: "Ocr SQS Queue Url" + Name: + Fn::Sub: "/formkiq/${AppEnvironment}/sqs/OcrQueueArn" + Type: String + Value: + Fn::GetAtt: + - OcrQueue + - Arn + Tags: + Application: + Fn::Sub: "FormKiQ ${FormKiQType}" + AppEnvironment: + Fn::Sub: "${AppEnvironment}" + StackName: + Fn::Sub: "${AWS::StackName}" + +Outputs: + #@overlay/match missing_ok=True + OcrBucket: + Value: + Ref: OcrBucket + #@overlay/match missing_ok=True + OcrQueue: + Value: + Ref: OcrQueue + #@overlay/match missing_ok=True + OcrQueueUrl: + Value: + Fn::GetAtt: + - OcrQueue + - QueueUrl + #@overlay/match missing_ok=True + OcrQueueArn: + Value: + Fn::GetAtt: + - OcrQueue + - Arn \ No newline at end of file diff --git a/lambda-api/src/main/resources/cloudformation/template-snippet.yaml b/lambda-api/src/main/resources/cloudformation/template.yaml similarity index 83% rename from lambda-api/src/main/resources/cloudformation/template-snippet.yaml rename to lambda-api/src/main/resources/cloudformation/template.yaml index d76af5e01..b4549e581 100644 --- a/lambda-api/src/main/resources/cloudformation/template-snippet.yaml +++ b/lambda-api/src/main/resources/cloudformation/template.yaml @@ -141,7 +141,7 @@ Resources: Type: AWS::Logs::LogGroup Properties: LogGroupName: - Fn::Sub: "/${AWS::StackName}/${DocumentsApiRequests}" + Fn::Sub: "/aws/vendedlogs/${AWS::StackName}/${DocumentsApiRequests}" RetentionInDays: 90 Tags: - Key: "Application" @@ -164,12 +164,14 @@ Resources: MemorySize: Fn::Sub: "${LambdaMemory}" CodeUri: ./lambda-api-graalvm.zip + Tracing: Active AutoPublishCodeSha256: #@ data.values.hash or assert.fail("missing version") Environment: Variables: APP_ENVIRONMENT: Fn::Sub: "${AppEnvironment}" DEBUG: false + ENABLE_AWS_X_RAY: true DOCUMENTS_TABLE: Fn::Sub: "{{resolve:ssm:/formkiq/${AppEnvironment}/dynamodb/DocumentsTableName}}" CACHE_TABLE: @@ -195,6 +197,12 @@ Resources: Ref: TypesenseApiEndpoint TYPESENSE_API_KEY: Ref: TypesenseApiKey + OCR_S3_BUCKET: + Ref: OcrBucket + OCR_SQS_QUEUE_URL: + Fn::GetAtt: + - OcrQueue + - QueueUrl MODULE_typesense: Fn::If: - CreateTypesenseResources @@ -216,29 +224,39 @@ Resources: Type: "AWS::Lambda::Permission" DependsOn: - DocumentsIamApi + - DocumentsKeyApi - DocumentsApiRequests Properties: Action: lambda:InvokeFunction FunctionName: Ref: DocumentsApiRequests Principal: apigateway.amazonaws.com - + DocumentsStageAccessLogs: Type: AWS::Logs::LogGroup Properties: RetentionInDays: 90 LogGroupName: - Fn::Sub: "/${AWS::StackName}/APIDocumentsHttpAccessLogs" + Fn::Sub: "/aws/vendedlogs/${AWS::StackName}/APIDocumentsHttpAccessLogs" IamDocumentsStageAccessLogs: Type: AWS::Logs::LogGroup Properties: RetentionInDays: 90 LogGroupName: - Fn::Sub: "/${AWS::StackName}/APIDocumentsIamAccessLogs" + Fn::Sub: "/aws/vendedlogs/${AWS::StackName}/APIDocumentsIamAccessLogs" + + KeyDocumentsStageAccessLogs: + Type: AWS::Logs::LogGroup + Properties: + RetentionInDays: 90 + LogGroupName: + Fn::Sub: "/aws/vendedlogs/${AWS::StackName}/APIDocumentsKeyAccessLogs" DocumentsStage: Type: AWS::ApiGatewayV2::Stage + DependsOn: + - DocumentsHttpApi Properties: ApiId: Ref: DocumentsHttpApi @@ -267,6 +285,8 @@ Resources: IamDocumentsStage: Type: AWS::ApiGatewayV2::Stage + DependsOn: + - DocumentsIamApi Properties: ApiId: Ref: DocumentsIamApi @@ -293,6 +313,36 @@ Resources: Stage: Ref: IamDocumentsStage + KeyDocumentsStage: + Type: AWS::ApiGatewayV2::Stage + DependsOn: + - DocumentsKeyApi + Properties: + ApiId: + Ref: DocumentsKeyApi + AutoDeploy: true + Description: + Fn::Sub: "Documents API Key ${AppEnvironment}" + StageName: "$default" + AccessLogSettings: + DestinationArn: + Fn::GetAtt: + - KeyDocumentsStageAccessLogs + - Arn + Format: '{ "requestId":"$context.requestId", "ip": "$context.identity.sourceIp", "requestTime":"$context.requestTime", "httpMethod":"$context.httpMethod","routeKey":"$context.routeKey", "status":"$context.status","protocol":"$context.protocol", "integrationStatus": $context.integrationStatus, "integrationLatency": $context.integrationLatency, "responseLength":"$context.responseLength" }' + + KeyDocumentsStageMapping: + Condition: HasCertificateStackName + Type: 'AWS::ApiGatewayV2::ApiMapping' + Properties: + DomainName: + Fn::ImportValue: + Fn::Sub: '${CertificateStackName}-KeyApiDomain' + ApiId: + Ref: DocumentsKeyApi + Stage: + Ref: KeyDocumentsStage + DocumentsHttpApiIdParameter: Type: AWS::SSM::Parameter Properties: @@ -382,6 +432,7 @@ Resources: - arn:aws:iam::aws:policy/AmazonAPIGatewayInvokeFullAccess - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole - arn:aws:iam::aws:policy/AmazonElasticFileSystemClientReadWriteAccess + - arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess Policies: - PolicyName: apigatewaylambdarole @@ -470,6 +521,26 @@ Resources: - sns:Publish Resource: - Fn::Sub: "{{resolve:ssm:/formkiq/${AppEnvironment}/sns/DocumentEventArn}}" + - Effect: Allow + Action: + - s3:GetObject + - s3:DeleteObject + - s3:ListBucket + - s3:PutObject + Resource: + Fn::Join: + - '' + - - 'arn:aws:s3:::' + - Ref: OcrBucket + - "/*" + - Effect: Allow + Action: + - s3:ListBucket + Resource: + Fn::Join: + - '' + - - 'arn:aws:s3:::' + - Ref: OcrBucket DocumentsIamApiUrlParameter: Type: AWS::SSM::Parameter @@ -511,6 +582,47 @@ Resources: Fn::Sub: "${AppEnvironment}" StackName: Fn::Sub: "${AWS::StackName}" + + DocumentsKeyApiUrlParameter: + Type: AWS::SSM::Parameter + Properties: + Description: "The URL for the API endpoint that uses API Key authorization" + Name: + Fn::Sub: "/formkiq/${AppEnvironment}/api/DocumentsKeyUrl" + Type: String + Value: + Fn::If: + - HasCertificateStackName + - Fn::Join: + - '' + - - 'https://' + - Fn::ImportValue: + Fn::Sub: '${CertificateStackName}-IamApiDomain' + - Fn::Sub: "https://${DocumentsKeyApi}.execute-api.${AWS::Region}.amazonaws.com" + Tags: + Application: + Fn::Sub: "FormKiQ ${FormKiQType}" + AppEnvironment: + Fn::Sub: "${AppEnvironment}" + StackName: + Fn::Sub: "${AWS::StackName}" + + DocumentsKeyApiIdParameter: + Type: AWS::SSM::Parameter + Properties: + Description: "The ID for the API endpoint that uses API Key authorization" + Name: + Fn::Sub: "/formkiq/${AppEnvironment}/api/DocumentsKeyId" + Type: String + Value: + Ref: DocumentsKeyApi + Tags: + Application: + Fn::Sub: "FormKiQ ${FormKiQType}" + AppEnvironment: + Fn::Sub: "${AppEnvironment}" + StackName: + Fn::Sub: "${AWS::StackName}" DocumentsApiRequestsParameter: Type: AWS::SSM::Parameter @@ -698,7 +810,18 @@ Outputs: - - 'https://' - Fn::ImportValue: Fn::Sub: '${CertificateStackName}-IamApiDomain' - - Fn::Sub: "https://${DocumentsIamApi}.execute-api.${AWS::Region}.amazonaws.com" + - Fn::Sub: "https://${DocumentsIamApi}.execute-api.${AWS::Region}.amazonaws.com" + DocumentsKeyApiUrl: + Description: "The endpoint url for the Key API" + Value: + Fn::If: + - HasCertificateStackName + - Fn::Join: + - '' + - - 'https://' + - Fn::ImportValue: + Fn::Sub: '${CertificateStackName}-KeyApiDomain' + - Fn::Sub: "https://${DocumentsKeyApi}.execute-api.${AWS::Region}.amazonaws.com" DocumentsHttpApiUrl: Description: "The endpoint url for the Http API" Value: diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/AbstractRequestHandler.java b/lambda-api/src/test/java/com/formkiq/stacks/api/AbstractRequestHandler.java index 952954ba0..a338b7727 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/AbstractRequestHandler.java +++ b/lambda-api/src/test/java/com/formkiq/stacks/api/AbstractRequestHandler.java @@ -30,9 +30,10 @@ import static com.formkiq.testutils.aws.TestServices.AWS_REGION; import static com.formkiq.testutils.aws.TestServices.BUCKET_NAME; import static com.formkiq.testutils.aws.TestServices.FORMKIQ_APP_ENVIRONMENT; +import static com.formkiq.testutils.aws.TestServices.OCR_BUCKET_NAME; import static com.formkiq.testutils.aws.TestServices.STAGE_BUCKET_NAME; import static com.formkiq.testutils.aws.TypeSenseExtension.API_KEY; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockserver.integration.ClientAndServer.startClientAndServer; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -214,6 +215,7 @@ public void before() throws Exception { this.map.put("CACHE_TABLE", CACHE_TABLE); this.map.put("DOCUMENTS_S3_BUCKET", BUCKET_NAME); this.map.put("STAGE_DOCUMENTS_S3_BUCKET", STAGE_BUCKET_NAME); + this.map.put("OCR_S3_BUCKET", OCR_BUCKET_NAME); this.map.put("SNS_DOCUMENT_EVENT", snsDocumentEvent); this.map.put("AWS_REGION", AWS_REGION.toString()); this.map.put("DEBUG", "true"); diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentSyncRequestHandlerTest.java b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentSyncRequestHandlerTest.java index 1b150d9b1..21320ebd6 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentSyncRequestHandlerTest.java +++ b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentSyncRequestHandlerTest.java @@ -24,8 +24,8 @@ package com.formkiq.stacks.api; import static com.formkiq.stacks.dynamodb.DocumentSyncService.MESSAGE_ADDED_METADATA; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import java.util.Arrays; import java.util.List; import java.util.Map; diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsActionsRequestTest.java b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsActionsRequestTest.java index 7458061ac..701253389 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsActionsRequestTest.java +++ b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsActionsRequestTest.java @@ -23,8 +23,8 @@ */ package com.formkiq.stacks.api; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import java.util.Arrays; import java.util.List; import java.util.Map; diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsFulltextRequestTest.java b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsFulltextRequestTest.java index e18e0f6a6..11e3d5ee9 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsFulltextRequestTest.java +++ b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsFulltextRequestTest.java @@ -23,7 +23,7 @@ */ package com.formkiq.stacks.api; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Arrays; import java.util.Map; import java.util.UUID; diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsOcrRequestTest.java b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsOcrRequestTest.java index 0d22bbd62..0da3715ac 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsOcrRequestTest.java +++ b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsOcrRequestTest.java @@ -23,7 +23,7 @@ */ package com.formkiq.stacks.api; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Arrays; import java.util.Map; import java.util.UUID; @@ -40,7 +40,7 @@ public class ApiDocumentsOcrRequestTest extends AbstractRequestHandler { /** - * DELETE /documents/{documentId}/ocr request. + * DELETE /documents/{documentId}/ocr request. TODO Save OCR, the verify deleted. * * @throws Exception an error has occurred */ @@ -62,7 +62,7 @@ public void testHandleDeleteDocumentOcr01() throws Exception { final int mapsize = 3; assertEquals(mapsize, m.size()); - assertEquals("402.0", String.valueOf(m.get("statusCode"))); + assertEquals("200.0", String.valueOf(m.get("statusCode"))); assertEquals(getHeaders(), "\"headers\":" + GsonUtil.getInstance().toJson(m.get("headers"))); } } @@ -89,7 +89,7 @@ public void testHandleGetDocumentOcr01() throws Exception { final int mapsize = 3; assertEquals(mapsize, m.size()); - assertEquals("402.0", String.valueOf(m.get("statusCode"))); + assertEquals("404.0", String.valueOf(m.get("statusCode"))); assertEquals(getHeaders(), "\"headers\":" + GsonUtil.getInstance().toJson(m.get("headers"))); } } @@ -117,7 +117,7 @@ public void testHandlePatchDocumentOcr01() throws Exception { final int mapsize = 3; assertEquals(mapsize, m.size()); - assertEquals("402.0", String.valueOf(m.get("statusCode"))); + assertEquals("404.0", String.valueOf(m.get("statusCode"))); assertEquals(getHeaders(), "\"headers\":" + GsonUtil.getInstance().toJson(m.get("headers"))); } } @@ -144,7 +144,7 @@ public void testHandlePostDocumentOcr01() throws Exception { final int mapsize = 3; assertEquals(mapsize, m.size()); - assertEquals("402.0", String.valueOf(m.get("statusCode"))); + assertEquals("404.0", String.valueOf(m.get("statusCode"))); assertEquals(getHeaders(), "\"headers\":" + GsonUtil.getInstance().toJson(m.get("headers"))); } } diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsOnlyOfficeRequestTest.java b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsOnlyOfficeRequestTest.java index b669f50c0..d9e1f0dae 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsOnlyOfficeRequestTest.java +++ b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsOnlyOfficeRequestTest.java @@ -23,7 +23,7 @@ */ package com.formkiq.stacks.api; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Arrays; import java.util.Map; import java.util.UUID; diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsPatchRequestTest.java b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsPatchRequestTest.java index b140c22d3..db44df59c 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsPatchRequestTest.java +++ b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsPatchRequestTest.java @@ -26,10 +26,10 @@ import static com.formkiq.aws.dynamodb.SiteIdKeyGenerator.createDatabaseKey; import static com.formkiq.stacks.api.handler.DocumentIdRequestHandler.FORMKIQ_DOC_EXT; import static com.formkiq.testutils.aws.TestServices.STAGE_BUCKET_NAME; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsPublicDocumentsRequestTest.java b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsPublicDocumentsRequestTest.java index 9c174859d..6021b7e0f 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsPublicDocumentsRequestTest.java +++ b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsPublicDocumentsRequestTest.java @@ -23,9 +23,9 @@ */ package com.formkiq.stacks.api; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import java.util.HashMap; import java.util.List; import java.util.Map; diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsRequestTest.java b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsRequestTest.java index 929362324..d066255ee 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsRequestTest.java +++ b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsRequestTest.java @@ -26,11 +26,11 @@ import static com.formkiq.aws.dynamodb.SiteIdKeyGenerator.DEFAULT_SITE_ID; import static com.formkiq.testutils.aws.TestServices.BUCKET_NAME; import static com.formkiq.testutils.aws.TestServices.STAGE_BUCKET_NAME; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -59,13 +59,13 @@ import com.formkiq.aws.dynamodb.model.DynamicDocumentItem; import com.formkiq.aws.dynamodb.model.SearchMetaCriteria; import com.formkiq.aws.dynamodb.model.SearchQuery; +import com.formkiq.aws.dynamodb.objects.DateUtil; import com.formkiq.aws.services.lambda.ApiGatewayRequestEvent; import com.formkiq.aws.services.lambda.ApiResponseError; import com.formkiq.lambda.apigateway.util.GsonUtil; import com.formkiq.module.lambdaservices.AwsServiceCache; import com.formkiq.plugins.tagschema.DocumentTagSchemaPlugin; import com.formkiq.plugins.tagschema.DocumentTagSchemaPluginExtension; -import com.formkiq.stacks.dynamodb.DateUtil; import com.formkiq.stacks.dynamodb.DocumentItemDynamoDb; import com.formkiq.stacks.dynamodb.DocumentSearchService; import com.formkiq.stacks.dynamodb.DocumentService; diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsSearchFulltextRequestTest.java b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsSearchFulltextRequestTest.java index ee8e0689d..a50d775b6 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsSearchFulltextRequestTest.java +++ b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsSearchFulltextRequestTest.java @@ -23,7 +23,7 @@ */ package com.formkiq.stacks.api; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Arrays; import java.util.Map; import java.util.UUID; diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsSearchRequestTest.java b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsSearchRequestTest.java index 5a4e0c005..7141e4fed 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsSearchRequestTest.java +++ b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsSearchRequestTest.java @@ -23,10 +23,10 @@ */ package com.formkiq.stacks.api; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsTagsRequestTest.java b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsTagsRequestTest.java index f9d6e959e..b5e526692 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsTagsRequestTest.java +++ b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsTagsRequestTest.java @@ -25,10 +25,10 @@ import static com.formkiq.stacks.dynamodb.DocumentService.MAX_RESULTS; import static com.formkiq.testutils.aws.TestServices.getSqsWebsocketQueueUrl; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.util.Arrays; diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsUploadRequestTest.java b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsUploadRequestTest.java index daaa79e93..7c7f0cbc8 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsUploadRequestTest.java +++ b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsUploadRequestTest.java @@ -23,11 +23,11 @@ */ package com.formkiq.stacks.api; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -44,7 +44,6 @@ import com.formkiq.aws.dynamodb.model.DocumentTag; import com.formkiq.aws.services.lambda.ApiGatewayRequestEvent; import com.formkiq.aws.services.lambda.ApiGatewayRequestEventBuilder; -import com.formkiq.aws.services.lambda.services.ConfigService; import com.formkiq.lambda.apigateway.util.GsonUtil; import com.formkiq.module.actions.Action; import com.formkiq.module.actions.ActionStatus; @@ -55,6 +54,7 @@ import com.formkiq.plugins.tagschema.DocumentTagSchemaPluginExtension; import com.formkiq.stacks.client.models.AddLargeDocument; import com.formkiq.stacks.client.models.DocumentActionType; +import com.formkiq.stacks.dynamodb.ConfigService; import com.formkiq.stacks.dynamodb.DocumentItemDynamoDb; import com.formkiq.testutils.aws.DynamoDbExtension; import com.formkiq.testutils.aws.LocalStackExtension; diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsVersionsRequestTest.java b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsVersionsRequestTest.java index 6d62eafae..268af6e7c 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsVersionsRequestTest.java +++ b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsVersionsRequestTest.java @@ -25,7 +25,7 @@ import static com.formkiq.aws.dynamodb.SiteIdKeyGenerator.createDatabaseKey; import static com.formkiq.testutils.aws.TestServices.BUCKET_NAME; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Map; diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiPrivateWebhooksRequestTest.java b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiPrivateWebhooksRequestTest.java index 2fe83abbf..f07157eab 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiPrivateWebhooksRequestTest.java +++ b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiPrivateWebhooksRequestTest.java @@ -25,8 +25,8 @@ import static com.formkiq.aws.dynamodb.SiteIdKeyGenerator.createDatabaseKey; import static com.formkiq.testutils.aws.TestServices.STAGE_BUCKET_NAME; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import java.util.Arrays; import java.util.Map; import java.util.UUID; diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiPublicWebhooksRequestTest.java b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiPublicWebhooksRequestTest.java index 6b58a94c2..9762da3a6 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiPublicWebhooksRequestTest.java +++ b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiPublicWebhooksRequestTest.java @@ -24,11 +24,11 @@ package com.formkiq.stacks.api; import static com.formkiq.aws.dynamodb.SiteIdKeyGenerator.createDatabaseKey; -import static com.formkiq.aws.services.lambda.services.ConfigService.DOCUMENT_TIME_TO_LIVE; +import static com.formkiq.stacks.dynamodb.ConfigService.DOCUMENT_TIME_TO_LIVE; import static com.formkiq.testutils.aws.TestServices.STAGE_BUCKET_NAME; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; import java.io.IOException; import java.time.ZoneOffset; import java.time.ZonedDateTime; @@ -44,8 +44,8 @@ import com.formkiq.aws.s3.S3ObjectMetadata; import com.formkiq.aws.services.lambda.ApiGatewayRequestEvent; import com.formkiq.aws.services.lambda.services.CacheService; -import com.formkiq.aws.services.lambda.services.ConfigService; import com.formkiq.lambda.apigateway.util.GsonUtil; +import com.formkiq.stacks.dynamodb.ConfigService; import com.formkiq.testutils.aws.DynamoDbExtension; import com.formkiq.testutils.aws.LocalStackExtension; diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiRequestHandlerTest.java b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiRequestHandlerTest.java index c12d652f9..56d4cf356 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiRequestHandlerTest.java +++ b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiRequestHandlerTest.java @@ -24,10 +24,10 @@ package com.formkiq.stacks.api; import static com.formkiq.aws.dynamodb.SiteIdKeyGenerator.DEFAULT_SITE_ID; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; @@ -210,7 +210,7 @@ public void testHandleGetRequest05() throws Exception { assertEquals(documentId, resp.get("documentId")); assertEquals(userId, resp.get("userId")); - assertNotNull(documentId, resp.get("insertedDate")); + assertNotNull(resp.get("insertedDate")); assertEquals(DEFAULT_SITE_ID, resp.get("siteId")); assertNull(resp.get("next")); assertNull(resp.get("previous")); @@ -264,7 +264,7 @@ public void testHandleGetRequest06() throws Exception { assertEquals(1, children.size()); assertEquals(documentId1, children.get(0).get("documentId")); - assertNotNull(userId, children.get(0).get("userId")); + assertEquals(userId, children.get(0).get("userId")); assertNotNull(children.get(0).get("belongsToDocumentId")); assertNotNull(children.get(0).get("insertedDate")); assertNull(children.get(0).get("siteId")); diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiTagSchemasRequestTest.java b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiTagSchemasRequestTest.java index b37a36d0b..e5a678734 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiTagSchemasRequestTest.java +++ b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiTagSchemasRequestTest.java @@ -23,7 +23,7 @@ */ package com.formkiq.stacks.api; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Arrays; import java.util.Map; import java.util.UUID; diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiWebhookIdRequestTest.java b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiWebhookIdRequestTest.java index 06cfcc67b..ab3f908f8 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiWebhookIdRequestTest.java +++ b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiWebhookIdRequestTest.java @@ -24,9 +24,9 @@ package com.formkiq.stacks.api; import static com.formkiq.testutils.aws.TestServices.FORMKIQ_APP_ENVIRONMENT; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import java.util.Arrays; import java.util.Date; import java.util.List; diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiWebhooksRequestTest.java b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiWebhooksRequestTest.java index 32fe3b938..bb40f54cc 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiWebhooksRequestTest.java +++ b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiWebhooksRequestTest.java @@ -23,14 +23,14 @@ */ package com.formkiq.stacks.api; -import static com.formkiq.aws.services.lambda.services.ConfigService.MAX_WEBHOOKS; -import static com.formkiq.aws.services.lambda.services.ConfigService.WEBHOOK_TIME_TO_LIVE; +import static com.formkiq.stacks.dynamodb.ConfigService.MAX_WEBHOOKS; +import static com.formkiq.stacks.dynamodb.ConfigService.WEBHOOK_TIME_TO_LIVE; import static com.formkiq.testutils.aws.TestServices.FORMKIQ_APP_ENVIRONMENT; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.time.Instant; import java.time.ZoneOffset; import java.time.ZonedDateTime; @@ -46,8 +46,8 @@ import com.formkiq.aws.dynamodb.PaginationResults; import com.formkiq.aws.dynamodb.model.DocumentTag; import com.formkiq.aws.services.lambda.ApiGatewayRequestEvent; -import com.formkiq.aws.services.lambda.services.ConfigService; import com.formkiq.lambda.apigateway.util.GsonUtil; +import com.formkiq.stacks.dynamodb.ConfigService; import com.formkiq.stacks.dynamodb.WebhooksService; import com.formkiq.testutils.aws.DynamoDbExtension; import com.formkiq.testutils.aws.LocalStackExtension; diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiWebhooksTagsRequestTest.java b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiWebhooksTagsRequestTest.java index df3d14888..46575249b 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiWebhooksTagsRequestTest.java +++ b/lambda-api/src/test/java/com/formkiq/stacks/api/ApiWebhooksTagsRequestTest.java @@ -23,8 +23,8 @@ */ package com.formkiq.stacks.api; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/ConfigurationApiKeysRequestTest.java b/lambda-api/src/test/java/com/formkiq/stacks/api/ConfigurationApiKeysRequestTest.java new file mode 100644 index 000000000..9065fffd9 --- /dev/null +++ b/lambda-api/src/test/java/com/formkiq/stacks/api/ConfigurationApiKeysRequestTest.java @@ -0,0 +1,248 @@ +/** + * MIT License + * + * Copyright (c) 2018 - 2020 FormKiQ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.formkiq.stacks.api; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import com.formkiq.aws.dynamodb.DynamicObject; +import com.formkiq.aws.services.lambda.ApiGatewayRequestEvent; +import com.formkiq.aws.services.lambda.ApiGatewayRequestEventBuilder; +import com.formkiq.lambda.apigateway.util.GsonUtil; +import com.formkiq.testutils.aws.DynamoDbExtension; +import com.formkiq.testutils.aws.LocalStackExtension; + +/** Unit Tests for request /configuration/apiKeys. */ +@ExtendWith(LocalStackExtension.class) +@ExtendWith(DynamoDbExtension.class) +public class ConfigurationApiKeysRequestTest extends AbstractRequestHandler { + + /** + * DELETE /configuration/apiKeys request. + * + * @param siteId {@link String} + * @param group {@link String} + * @param apiKey {@link String} + * @return {@link ApiGatewayRequestEvent} + */ + private ApiGatewayRequestEvent deleteRequest(final String siteId, final String group, + final String apiKey) { + ApiGatewayRequestEvent event = new ApiGatewayRequestEventBuilder().method("delete") + .resource("/configuration/apiKeys").path("/configuration/apiKeys").group(group) + .user("joesmith").queryParameters(siteId != null ? Map.of("siteId", siteId) : null) + .queryParameters(Map.of("apiKey", apiKey)).build(); + return event; + } + + /** + * Get /configuration/apiKeys request. + * + * @param siteId {@link String} + * @param group {@link String} + * @return {@link ApiGatewayRequestEvent} + */ + private ApiGatewayRequestEvent getRequest(final String siteId, final String group) { + ApiGatewayRequestEvent event = new ApiGatewayRequestEventBuilder().method("get") + .resource("/configuration/apiKeys").path("/configuration/apiKeys").group(group) + .user("joesmith").queryParameters(siteId != null ? Map.of("siteId", siteId) : null).build(); + return event; + } + + /** + * POST /configuration/apiKeys request. + * + * @param siteId {@link String} + * @param group {@link String} + * @param body {@link String} + * @return {@link ApiGatewayRequestEvent} + */ + private ApiGatewayRequestEvent postRequest(final String siteId, final String group, + final String body) { + ApiGatewayRequestEvent event = + new ApiGatewayRequestEventBuilder().method("post").resource("/configuration/apiKeys") + .path("/configuration/apiKeys").group(group).user("joesmith") + .queryParameters(siteId != null ? Map.of("siteId", siteId) : null).body(body).build(); + return event; + } + + /** + * Delete /configuration/apiKeys default as User. + * + * @throws Exception an error has occurred + */ + @SuppressWarnings("unchecked") + @Test + public void testHandleDeleteApiKeys01() throws Exception { + // given + String siteId = null; + String group = "default"; + + ApiGatewayRequestEvent event = deleteRequest(siteId, group, "ABC"); + + // when + String response = handleRequest(event); + + // then + Map m = GsonUtil.getInstance().fromJson(response, Map.class); + + final int mapsize = 3; + assertEquals(mapsize, m.size()); + assertEquals("401.0", String.valueOf(m.get("statusCode"))); + assertEquals(getHeaders(), "\"headers\":" + GsonUtil.getInstance().toJson(m.get("headers"))); + assertEquals("{\"message\":\"user is unauthorized\"}", String.valueOf(m.get("body"))); + } + + /** + * POST /configuration/apiKeys default as User. + * + * @throws Exception an error has occurred + */ + @SuppressWarnings("unchecked") + @Test + public void testHandlePostApiKeys01() throws Exception { + // given + String siteId = null; + String group = "default"; + + String body = GsonUtil.getInstance().toJson(Map.of("name", "test key")); + ApiGatewayRequestEvent event = postRequest(siteId, group, body); + + // when + String response = handleRequest(event); + + // then + Map m = GsonUtil.getInstance().fromJson(response, Map.class); + + final int mapsize = 3; + assertEquals(mapsize, m.size()); + assertEquals("401.0", String.valueOf(m.get("statusCode"))); + assertEquals(getHeaders(), "\"headers\":" + GsonUtil.getInstance().toJson(m.get("headers"))); + assertEquals("{\"message\":\"user is unauthorized\"}", String.valueOf(m.get("body"))); + } + + /** + * Get/POST/DELETE /configuration/apiKeys default as Admin. + * + * @throws Exception an error has occurred + */ + @SuppressWarnings("unchecked") + @Test + public void testHandleGetApiKeys01() throws Exception { + // given + String siteId = null; + String group = "Admins"; + + String body = GsonUtil.getInstance().toJson(Map.of("name", "test key")); + ApiGatewayRequestEvent eventPost = postRequest(siteId, group, body); + + // when + String responsePost = handleRequest(eventPost); + + // then + Map mpost = GsonUtil.getInstance().fromJson(responsePost, Map.class); + verifyResponse(mpost); + + DynamicObject resp = new DynamicObject(fromJson(mpost.get("body"), Map.class)); + assertEquals(2, resp.size()); + assertEquals("test key", resp.getString("name")); + assertNotNull(resp.getString("apiKey")); + + // given + ApiGatewayRequestEvent eventGet = getRequest(siteId, group); + + // when + String responseGet = handleRequest(eventGet); + + // then + Map mget = GsonUtil.getInstance().fromJson(responseGet, Map.class); + verifyResponse(mget); + + resp = new DynamicObject(fromJson(mget.get("body"), Map.class)); + List list = resp.getList("apiKeys"); + assertEquals(1, list.size()); + String apiKey = list.get(0).get("apiKey").toString(); + assertTrue(apiKey.contains("**************")); + assertEquals("test key", list.get(0).getString("name")); + assertNotNull(list.get(0).get("insertedDate")); + assertNull(list.get(0).get("PK")); + assertNull(list.get(0).get("SK")); + + // given + ApiGatewayRequestEvent eventDelete = deleteRequest(siteId, group, apiKey); + + // when + String responseDelete = handleRequest(eventDelete); + + // then + Map mdelete = GsonUtil.getInstance().fromJson(responseDelete, Map.class); + verifyResponse(mdelete); + + responseGet = handleRequest(eventGet); + mget = GsonUtil.getInstance().fromJson(responseGet, Map.class); + verifyResponse(mget); + + resp = new DynamicObject(fromJson(mget.get("body"), Map.class)); + list = resp.getList("apiKeys"); + assertEquals(0, list.size()); + } + + /** + * Get /configuration/apiKeys default as User. + * + * @throws Exception an error has occurred + */ + @SuppressWarnings("unchecked") + @Test + public void testHandleGetApiKeys02() throws Exception { + // given + String siteId = null; + String group = "default"; + + ApiGatewayRequestEvent event = getRequest(siteId, group); + + // when + String response = handleRequest(event); + + // then + Map m = GsonUtil.getInstance().fromJson(response, Map.class); + + final int mapsize = 3; + assertEquals(mapsize, m.size()); + assertEquals("401.0", String.valueOf(m.get("statusCode"))); + assertEquals(getHeaders(), "\"headers\":" + GsonUtil.getInstance().toJson(m.get("headers"))); + assertEquals("{\"message\":\"user is unauthorized\"}", String.valueOf(m.get("body"))); + } + + private void verifyResponse(final Map m) { + final int mapsize = 3; + assertEquals(mapsize, m.size()); + assertEquals("200.0", String.valueOf(m.get("statusCode"))); + assertEquals(getHeaders(), "\"headers\":" + GsonUtil.getInstance().toJson(m.get("headers"))); + } +} diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/ConfigurationRequestTest.java b/lambda-api/src/test/java/com/formkiq/stacks/api/ConfigurationRequestTest.java new file mode 100644 index 000000000..cbf003790 --- /dev/null +++ b/lambda-api/src/test/java/com/formkiq/stacks/api/ConfigurationRequestTest.java @@ -0,0 +1,283 @@ +/** + * MIT License + * + * Copyright (c) 2018 - 2020 FormKiQ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.formkiq.stacks.api; + +import static com.formkiq.stacks.dynamodb.ConfigService.CHATGPT_API_KEY; +import static org.junit.jupiter.api.Assertions.assertEquals; +import java.util.Map; +import java.util.UUID; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import com.formkiq.aws.dynamodb.DynamicObject; +import com.formkiq.aws.services.lambda.ApiGatewayRequestEvent; +import com.formkiq.aws.services.lambda.ApiGatewayRequestEventBuilder; +import com.formkiq.lambda.apigateway.util.GsonUtil; +import com.formkiq.stacks.dynamodb.ConfigService; +import com.formkiq.testutils.aws.DynamoDbExtension; +import com.formkiq.testutils.aws.LocalStackExtension; + +/** Unit Tests for request /configuration. */ +@ExtendWith(LocalStackExtension.class) +@ExtendWith(DynamoDbExtension.class) +public class ConfigurationRequestTest extends AbstractRequestHandler { + + /** + * Get /configuration request. + * + * @param siteId {@link String} + * @param group {@link String} + * @return {@link ApiGatewayRequestEvent} + */ + private ApiGatewayRequestEvent getRequest(final String siteId, final String group) { + ApiGatewayRequestEvent event = new ApiGatewayRequestEventBuilder().method("get") + .resource("/configuration").path("/configuration").group(group).user("joesmith") + .queryParameters(siteId != null ? Map.of("siteId", siteId) : null).build(); + return event; + } + + /** + * Patch /configuration request. + * + * @param siteId {@link String} + * @param group {@link String} + * @param body {@link String} + * @return {@link ApiGatewayRequestEvent} + */ + private ApiGatewayRequestEvent patchRequest(final String siteId, final String group, + final String body) { + ApiGatewayRequestEvent event = new ApiGatewayRequestEventBuilder().method("patch") + .resource("/configuration").path("/configuration").group(group).user("joesmith") + .queryParameters(siteId != null ? Map.of("siteId", siteId) : null).body(body).build(); + return event; + } + + + /** + * Get /config default as Admin. + * + * @throws Exception an error has occurred + */ + @SuppressWarnings("unchecked") + @Test + public void testHandleGetSites01() throws Exception { + // given + String siteId = null; + String group = "Admins"; + + ConfigService config = getAwsServices().getExtension(ConfigService.class); + config.save(siteId, new DynamicObject(Map.of("chatGptApiKey", "somevalue"))); + + String body = GsonUtil.getInstance().toJson(Map.of("chatGptApiKey", "anothervalue")); + ApiGatewayRequestEvent eventPatch = patchRequest(siteId, group, body); + + ApiGatewayRequestEvent eventGet = getRequest(siteId, group); + + // when + String responsePatch = handleRequest(eventPatch); + String responseGet = handleRequest(eventGet); + + // then + Map mpatch = GsonUtil.getInstance().fromJson(responsePatch, Map.class); + verifyResponse(mpatch); + + Map mget = GsonUtil.getInstance().fromJson(responseGet, Map.class); + verifyResponse(mget); + + final int expected = 4; + DynamicObject resp = new DynamicObject(fromJson(mget.get("body"), Map.class)); + assertEquals(expected, resp.size()); + assertEquals("anothervalue", resp.getString("chatGptApiKey")); + assertEquals("", resp.getString("maxContentLengthBytes")); + assertEquals("", resp.getString("maxDocuments")); + assertEquals("", resp.getString("maxWebhooks")); + } + + /** + * Get /config default as User. + * + * @throws Exception an error has occurred + */ + @SuppressWarnings("unchecked") + @Test + public void testHandleGetSites02() throws Exception { + // given + String siteId = null; + String group = "default"; + + ApiGatewayRequestEvent event = getRequest(siteId, group); + + // when + String response = handleRequest(event); + + // then + Map m = GsonUtil.getInstance().fromJson(response, Map.class); + + final int mapsize = 3; + assertEquals(mapsize, m.size()); + assertEquals("401.0", String.valueOf(m.get("statusCode"))); + assertEquals(getHeaders(), "\"headers\":" + GsonUtil.getInstance().toJson(m.get("headers"))); + assertEquals("{\"message\":\"user is unauthorized\"}", String.valueOf(m.get("body"))); + } + + /** + * Get /config for siteId, Config in default. + * + * @throws Exception an error has occurred + */ + @SuppressWarnings("unchecked") + @Test + public void testHandleGetSites03() throws Exception { + // given + String siteId = UUID.randomUUID().toString(); + String group = "Admins"; + + ConfigService config = getAwsServices().getExtension(ConfigService.class); + config.save(null, new DynamicObject(Map.of(CHATGPT_API_KEY, "somevalue"))); + + ApiGatewayRequestEvent event = getRequest(siteId, group); + + // when + String response = handleRequest(event); + + // then + Map m = GsonUtil.getInstance().fromJson(response, Map.class); + + verifyResponse(m); + + final int expected = 4; + DynamicObject resp = new DynamicObject(fromJson(m.get("body"), Map.class)); + assertEquals(expected, resp.size()); + assertEquals("somevalue", resp.getString("chatGptApiKey")); + assertEquals("", resp.getString("maxContentLengthBytes")); + assertEquals("", resp.getString("maxDocuments")); + assertEquals("", resp.getString("maxWebhooks")); + } + + /** + * Get /config for siteId, Config in siteId. + * + * @throws Exception an error has occurred + */ + @SuppressWarnings("unchecked") + @Test + public void testHandleGetSites04() throws Exception { + // given + String siteId = UUID.randomUUID().toString(); + String group = "Admins"; + + ConfigService config = getAwsServices().getExtension(ConfigService.class); + config.save(null, new DynamicObject(Map.of(CHATGPT_API_KEY, "somevalue"))); + config.save(siteId, new DynamicObject(Map.of(CHATGPT_API_KEY, "anothervalue"))); + + ApiGatewayRequestEvent event = getRequest(siteId, group); + + // when + String response = handleRequest(event); + + // then + Map m = GsonUtil.getInstance().fromJson(response, Map.class); + + verifyResponse(m); + + final int expected = 4; + DynamicObject resp = new DynamicObject(fromJson(m.get("body"), Map.class)); + assertEquals(expected, resp.size()); + assertEquals("anothervalue", resp.getString("chatGptApiKey")); + assertEquals("", resp.getString("maxContentLengthBytes")); + assertEquals("", resp.getString("maxDocuments")); + assertEquals("", resp.getString("maxWebhooks")); + } + + /** + * PUT /config default as Admin. + * + * @throws Exception an error has occurred + */ + @SuppressWarnings("unchecked") + @Test + public void testHandlePutSites01() throws Exception { + // given + String siteId = null; + String group = "Admins"; + + String body = GsonUtil.getInstance().toJson(Map.of("chatGptApiKey", "anotherkey", + "maxContentLengthBytes", "1000000", "maxDocuments", "1000", "maxWebhooks", "5")); + ApiGatewayRequestEvent eventPatch = patchRequest(siteId, group, body); + + ApiGatewayRequestEvent eventGet = getRequest(siteId, group); + + // when + String responsePatch = handleRequest(eventPatch); + String responseGet = handleRequest(eventGet); + + // then + Map mpatch = GsonUtil.getInstance().fromJson(responsePatch, Map.class); + verifyResponse(mpatch); + + Map mget = GsonUtil.getInstance().fromJson(responseGet, Map.class); + verifyResponse(mget); + + final int expected = 4; + DynamicObject resp = new DynamicObject(fromJson(mget.get("body"), Map.class)); + assertEquals(expected, resp.size()); + assertEquals("anotherkey", resp.getString("chatGptApiKey")); + assertEquals("1000000", resp.getString("maxContentLengthBytes")); + assertEquals("1000", resp.getString("maxDocuments")); + assertEquals("5", resp.getString("maxWebhooks")); + } + + /** + * PUT /config default as user. + * + * @throws Exception an error has occurred + */ + @SuppressWarnings("unchecked") + @Test + public void testHandlePutSites02() throws Exception { + // given + String siteId = null; + String group = "default"; + + String body = + GsonUtil.getInstance().toJson(Map.of("key", "anotherkey", "value", "anothervalue")); + ApiGatewayRequestEvent eventPatch = patchRequest(siteId, group, body); + + // when + String responsePatch = handleRequest(eventPatch); + + // then + final int mapsize = 3; + Map m = GsonUtil.getInstance().fromJson(responsePatch, Map.class); + assertEquals(mapsize, m.size()); + assertEquals("401.0", String.valueOf(m.get("statusCode"))); + assertEquals(getHeaders(), "\"headers\":" + GsonUtil.getInstance().toJson(m.get("headers"))); + } + + private void verifyResponse(final Map m) { + final int mapsize = 3; + assertEquals(mapsize, m.size()); + assertEquals("200.0", String.valueOf(m.get("statusCode"))); + assertEquals(getHeaders(), "\"headers\":" + GsonUtil.getInstance().toJson(m.get("headers"))); + } +} diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/DocumentIdContentGetRequestHandlerTest.java b/lambda-api/src/test/java/com/formkiq/stacks/api/DocumentIdContentGetRequestHandlerTest.java index 337fba24f..1c44d7726 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/DocumentIdContentGetRequestHandlerTest.java +++ b/lambda-api/src/test/java/com/formkiq/stacks/api/DocumentIdContentGetRequestHandlerTest.java @@ -26,8 +26,8 @@ import static com.formkiq.aws.dynamodb.SiteIdKeyGenerator.createS3Key; import static com.formkiq.testutils.aws.TestServices.AWS_REGION; import static com.formkiq.testutils.aws.TestServices.BUCKET_NAME; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/DocumentIdUrlGetRequestHandlerTest.java b/lambda-api/src/test/java/com/formkiq/stacks/api/DocumentIdUrlGetRequestHandlerTest.java index fd394d250..bde761c41 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/DocumentIdUrlGetRequestHandlerTest.java +++ b/lambda-api/src/test/java/com/formkiq/stacks/api/DocumentIdUrlGetRequestHandlerTest.java @@ -24,9 +24,9 @@ package com.formkiq.stacks.api; import static com.formkiq.testutils.aws.TestServices.AWS_REGION; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/IndicesFolderMoveRequestTest.java b/lambda-api/src/test/java/com/formkiq/stacks/api/IndicesFolderMoveRequestTest.java index a90ba617e..23e2c1eff 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/IndicesFolderMoveRequestTest.java +++ b/lambda-api/src/test/java/com/formkiq/stacks/api/IndicesFolderMoveRequestTest.java @@ -23,7 +23,7 @@ */ package com.formkiq.stacks.api; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Arrays; import java.util.Date; import java.util.Map; diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/IndicesSearchRequestTest.java b/lambda-api/src/test/java/com/formkiq/stacks/api/IndicesSearchRequestTest.java index 889f0bc98..5e0ea83c9 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/IndicesSearchRequestTest.java +++ b/lambda-api/src/test/java/com/formkiq/stacks/api/IndicesSearchRequestTest.java @@ -24,7 +24,7 @@ package com.formkiq.stacks.api; import static com.formkiq.testutils.aws.DynamoDbExtension.DOCUMENTS_TABLE; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Arrays; import java.util.Collection; import java.util.Date; diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsSitesRequestTest.java b/lambda-api/src/test/java/com/formkiq/stacks/api/SitesRequestTest.java similarity index 86% rename from lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsSitesRequestTest.java rename to lambda-api/src/test/java/com/formkiq/stacks/api/SitesRequestTest.java index c42186ffd..8435649d9 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/ApiDocumentsSitesRequestTest.java +++ b/lambda-api/src/test/java/com/formkiq/stacks/api/SitesRequestTest.java @@ -24,13 +24,13 @@ package com.formkiq.stacks.api; import static com.formkiq.aws.dynamodb.SiteIdKeyGenerator.DEFAULT_SITE_ID; -import static com.formkiq.aws.services.lambda.services.ConfigService.MAX_DOCUMENTS; -import static com.formkiq.aws.services.lambda.services.ConfigService.MAX_WEBHOOKS; +import static com.formkiq.stacks.dynamodb.ConfigService.MAX_DOCUMENTS; +import static com.formkiq.stacks.dynamodb.ConfigService.MAX_WEBHOOKS; import static com.formkiq.testutils.aws.TestServices.FORMKIQ_APP_ENVIRONMENT; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.List; import java.util.Map; import java.util.regex.Pattern; @@ -38,19 +38,34 @@ import org.junit.jupiter.api.extension.ExtendWith; import com.formkiq.aws.dynamodb.DynamicObject; import com.formkiq.aws.services.lambda.ApiGatewayRequestEvent; -import com.formkiq.aws.services.lambda.services.ConfigService; +import com.formkiq.aws.services.lambda.ApiGatewayRequestEventBuilder; import com.formkiq.lambda.apigateway.util.GsonUtil; +import com.formkiq.stacks.dynamodb.ConfigService; import com.formkiq.testutils.aws.DynamoDbExtension; import com.formkiq.testutils.aws.LocalStackExtension; /** Unit Tests for request /sites. */ @ExtendWith(LocalStackExtension.class) @ExtendWith(DynamoDbExtension.class) -public class ApiDocumentsSitesRequestTest extends AbstractRequestHandler { +public class SitesRequestTest extends AbstractRequestHandler { /** Email Pattern. */ private static final String EMAIL = "[abcdefghijklmnopqrstuvwxyz0123456789]{8}"; + /** + * Get /esignature/docusign/config request. + * + * @param siteId {@link String} + * @param group {@link String} + * @return {@link ApiGatewayRequestEvent} + */ + private ApiGatewayRequestEvent getRequest(final String siteId, final String group) { + ApiGatewayRequestEvent event = new ApiGatewayRequestEventBuilder().method("get") + .resource("/sites").path("/sites").group(group).user("joesmith") + .queryParameters(siteId != null ? Map.of("siteId", siteId) : null).build(); + return event; + } + /** * Get /sites with SES support. * @@ -61,7 +76,7 @@ public class ApiDocumentsSitesRequestTest extends AbstractRequestHandler { public void testHandleGetSites01() throws Exception { // given putSsmParameter("/formkiq/" + FORMKIQ_APP_ENVIRONMENT + "/maildomain", "tryformkiq.com"); - ApiGatewayRequestEvent event = toRequestEvent("/request-get-sites01.json"); + ApiGatewayRequestEvent event = getRequest(null, "default Admins finance"); // when String response = handleRequest(event); @@ -120,7 +135,7 @@ public void testHandleGetSites01() throws Exception { public void testHandleGetSites02() throws Exception { // given removeSsmParameter("/formkiq/" + FORMKIQ_APP_ENVIRONMENT + "/maildomain"); - ApiGatewayRequestEvent event = toRequestEvent("/request-get-sites01.json"); + ApiGatewayRequestEvent event = getRequest(null, "default Admins finance"); // when String response = handleRequest(event); @@ -161,8 +176,7 @@ public void testHandleGetSites03() throws Exception { putSsmParameter("/formkiq/" + FORMKIQ_APP_ENVIRONMENT + "/maildomain", "tryformkiq.com"); removeSsmParameter( String.format("/formkiq/%s/siteid/%s/email", FORMKIQ_APP_ENVIRONMENT, "default")); - ApiGatewayRequestEvent event = toRequestEvent("/request-get-sites01.json"); - setCognitoGroup(event, "default_read finance"); + ApiGatewayRequestEvent event = getRequest(null, "default_read finance"); // when String response = handleRequest(event); @@ -212,8 +226,8 @@ public void testHandleGetSites03() throws Exception { public void testHandleGetSites04() throws Exception { // given String siteId = "finance"; - ApiGatewayRequestEvent event = toRequestEvent("/request-get-sites01.json"); - setCognitoGroup(event, siteId); + ApiGatewayRequestEvent event = getRequest(siteId, siteId); + ConfigService configService = getAwsServices().getExtension(ConfigService.class); configService.save(siteId, new DynamicObject(Map.of(MAX_DOCUMENTS, "5", MAX_WEBHOOKS, "10"))); diff --git a/lambda-api/src/test/java/com/formkiq/stacks/api/handler/DocumentsRestrictionsMaxContentLengthTest.java b/lambda-api/src/test/java/com/formkiq/stacks/api/handler/DocumentsRestrictionsMaxContentLengthTest.java index f529bb248..2363c627d 100644 --- a/lambda-api/src/test/java/com/formkiq/stacks/api/handler/DocumentsRestrictionsMaxContentLengthTest.java +++ b/lambda-api/src/test/java/com/formkiq/stacks/api/handler/DocumentsRestrictionsMaxContentLengthTest.java @@ -24,8 +24,8 @@ package com.formkiq.stacks.api.handler; import static com.formkiq.testutils.aws.DynamoDbExtension.DOCUMENTS_TABLE; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.net.URISyntaxException; import java.util.Map; @@ -36,9 +36,9 @@ import com.formkiq.aws.dynamodb.DynamicObject; import com.formkiq.aws.dynamodb.DynamoDbConnectionBuilder; import com.formkiq.aws.dynamodb.DynamoDbConnectionBuilderExtension; -import com.formkiq.aws.services.lambda.services.ConfigService; import com.formkiq.module.lambdaservices.AwsServiceCache; import com.formkiq.stacks.api.CoreAwsServiceCache; +import com.formkiq.stacks.dynamodb.ConfigService; import com.formkiq.testutils.aws.DynamoDbExtension; import com.formkiq.testutils.aws.DynamoDbTestServices; diff --git a/lambda-api/src/test/resources/ocr/receipt.png b/lambda-api/src/test/resources/ocr/receipt.png new file mode 100644 index 000000000..dd0ffd842 Binary files /dev/null and b/lambda-api/src/test/resources/ocr/receipt.png differ diff --git a/lambda-api/src/test/resources/request-get-sites01.json b/lambda-api/src/test/resources/request-get-sites01.json deleted file mode 100644 index 8137f8300..000000000 --- a/lambda-api/src/test/resources/request-get-sites01.json +++ /dev/null @@ -1,131 +0,0 @@ -{ - "version": "1.0", - "resource": "/sites", - "path": "/sites", - "httpMethod": "GET", - "headers": { - "Content-Length": "0", - "Host": "0jau435s9j.execute-api.us-east-1.amazonaws.com", - "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:78.0) Gecko/20100101 Firefox/78.0", - "X-Amzn-Trace-Id": "Root=1-5f069100-55e2aba102cf8a5c579dbd65", - "X-Forwarded-For": "104.246.140.89", - "X-Forwarded-Port": "443", - "X-Forwarded-Proto": "https", - "accept": "application/json, text/plain, */*", - "accept-encoding": "gzip, deflate, br", - "accept-language": "en-US,en;q=0.5", - "authorization": "eyJraWQiOiJLYVBVXC81Y2Jmdk03dXkyWktzaEtyNTNYTWg2emhPS3c1RFR0bVYrRWV2Zz0iLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiI5ZDdlODM1Mi0yMTQyLTQzNjMtOWE2Ni00NWE2NWRhYWEwZGUiLCJjb2duaXRvOmdyb3VwcyI6WyJVc2VycyIsIkFkbWlucyJdLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiY29nbml0bzpwcmVmZXJyZWRfcm9sZSI6ImFybjphd3M6aWFtOjo2MjI2NTM4NjUyNzc6cm9sZVwvZm9ybWtpcS1zdGFja3MtZG9jdW1lbnQtdW5pdHRlc3QtRG8tQWRtaW5Hcm91cFJvbGUtRkQ4M1pHTUtSNktQIiwiaXNzIjoiaHR0cHM6XC9cL2NvZ25pdG8taWRwLnVzLWVhc3QtMS5hbWF6b25hd3MuY29tXC91cy1lYXN0LTFfM3ZXVTBqbkdwIiwiY29nbml0bzp1c2VybmFtZSI6Im1mcmllc2VuQGdtYWlsLmNvbSIsImNvZ25pdG86cm9sZXMiOlsiYXJuOmF3czppYW06OjYyMjY1Mzg2NTI3Nzpyb2xlXC9mb3Jta2lxLXN0YWNrcy1kb2N1bWVudC11bml0dGVzdC1Eby1BZG1pbkdyb3VwUm9sZS1GRDgzWkdNS1I2S1AiXSwiYXVkIjoibWFuMnFpaGIyMGV1bXZyNTVwcWN1dnA2biIsImV2ZW50X2lkIjoiNjY1ZjA3MTctNGZiYy00NTExLTkxMmItNmNiNmY1NjZlMWMwIiwidG9rZW5fdXNlIjoiaWQiLCJhdXRoX3RpbWUiOjE1OTQyNjU4MjYsImV4cCI6MTU5NDI2OTQyNiwiaWF0IjoxNTk0MjY1ODI2LCJlbWFpbCI6Im1mcmllc2VuQGdtYWlsLmNvbSJ9.fvG4okaL6c9dhjCbfaLNHiR9AMW5Dqfe0KIZGsuQ6ELsUZpZ2royqInHW0R8xkAw1NTsgRH4NvDPxwHB7uup3OxK15a1mg0tVZ4rG_bITURsRDa0MHWFl-9cJAbBXkYAfoYCS3eXc9eIZPyzlhXJDzGy-O_UaOArnz1PvbOdgKK_N3jSBm7UaiPhu8ug1hea1P6IFiGYfLPhqsMqhGc8RJdIqQH-oJbDGkyukxyWT-8WFS6jJRZL_L-WPG16Y1xCsuhJdblUjki-pyTJa_0k7P6Rma1h7eBaw-GDzLthdTC4eMZUof-ICNRS6x1qWSY9tA-YOuvahqc5yROUSprAEQ", - "dnt": "1", - "origin": "https://d2viwvgttu39g1.cloudfront.net", - "referer": "https://d2viwvgttu39g1.cloudfront.net/documents/explore" - }, - "multiValueHeaders": { - "Content-Length": [ - "0" - ], - "Host": [ - "0jau435s9j.execute-api.us-east-1.amazonaws.com" - ], - "User-Agent": [ - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:78.0) Gecko/20100101 Firefox/78.0" - ], - "X-Amzn-Trace-Id": [ - "Root=1-5f069100-55e2aba102cf8a5c579dbd65" - ], - "X-Forwarded-For": [ - "104.246.140.89" - ], - "X-Forwarded-Port": [ - "443" - ], - "X-Forwarded-Proto": [ - "https" - ], - "accept": [ - "application/json, text/plain, */*" - ], - "accept-encoding": [ - "gzip, deflate, br" - ], - "accept-language": [ - "en-US,en;q=0.5" - ], - "authorization": [ - "eyJraWQiOiJLYVBVXC81Y2Jmdk03dXkyWktzaEtyNTNYTWg2emhPS3c1RFR0bVYrRWV2Zz0iLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiI5ZDdlODM1Mi0yMTQyLTQzNjMtOWE2Ni00NWE2NWRhYWEwZGUiLCJjb2duaXRvOmdyb3VwcyI6WyJVc2VycyIsIkFkbWlucyJdLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiY29nbml0bzpwcmVmZXJyZWRfcm9sZSI6ImFybjphd3M6aWFtOjo2MjI2NTM4NjUyNzc6cm9sZVwvZm9ybWtpcS1zdGFja3MtZG9jdW1lbnQtdW5pdHRlc3QtRG8tQWRtaW5Hcm91cFJvbGUtRkQ4M1pHTUtSNktQIiwiaXNzIjoiaHR0cHM6XC9cL2NvZ25pdG8taWRwLnVzLWVhc3QtMS5hbWF6b25hd3MuY29tXC91cy1lYXN0LTFfM3ZXVTBqbkdwIiwiY29nbml0bzp1c2VybmFtZSI6Im1mcmllc2VuQGdtYWlsLmNvbSIsImNvZ25pdG86cm9sZXMiOlsiYXJuOmF3czppYW06OjYyMjY1Mzg2NTI3Nzpyb2xlXC9mb3Jta2lxLXN0YWNrcy1kb2N1bWVudC11bml0dGVzdC1Eby1BZG1pbkdyb3VwUm9sZS1GRDgzWkdNS1I2S1AiXSwiYXVkIjoibWFuMnFpaGIyMGV1bXZyNTVwcWN1dnA2biIsImV2ZW50X2lkIjoiNjY1ZjA3MTctNGZiYy00NTExLTkxMmItNmNiNmY1NjZlMWMwIiwidG9rZW5fdXNlIjoiaWQiLCJhdXRoX3RpbWUiOjE1OTQyNjU4MjYsImV4cCI6MTU5NDI2OTQyNiwiaWF0IjoxNTk0MjY1ODI2LCJlbWFpbCI6Im1mcmllc2VuQGdtYWlsLmNvbSJ9.fvG4okaL6c9dhjCbfaLNHiR9AMW5Dqfe0KIZGsuQ6ELsUZpZ2royqInHW0R8xkAw1NTsgRH4NvDPxwHB7uup3OxK15a1mg0tVZ4rG_bITURsRDa0MHWFl-9cJAbBXkYAfoYCS3eXc9eIZPyzlhXJDzGy-O_UaOArnz1PvbOdgKK_N3jSBm7UaiPhu8ug1hea1P6IFiGYfLPhqsMqhGc8RJdIqQH-oJbDGkyukxyWT-8WFS6jJRZL_L-WPG16Y1xCsuhJdblUjki-pyTJa_0k7P6Rma1h7eBaw-GDzLthdTC4eMZUof-ICNRS6x1qWSY9tA-YOuvahqc5yROUSprAEQ" - ], - "dnt": [ - "1" - ], - "origin": [ - "https://d2viwvgttu39g1.cloudfront.net" - ], - "referer": [ - "https://d2viwvgttu39g1.cloudfront.net/documents/explore" - ] - }, - "queryStringParameters": { - "date": "2020-07-08", - "tz": "-05:00" - }, - "multiValueQueryStringParameters": { - "date": [ - "2020-07-08" - ], - "tz": [ - "-05:00" - ] - }, - "requestContext": { - "accountId": "1111111111111111", - "apiId": "0jau435s9j", - "authorizer": { - "claims": { - "aud": "man2qihb20eumvr55pqcuvp6n", - "auth_time": "1594265826", - "cognito:groups": "[default Admins finance]", - "cognito:preferred_role": "arn:aws:iam::1111111111111111:role/formkiq-stacks-document-unittest-Do-AdminGroupRole-FD83ZGMKR6KP", - "cognito:roles": "[arn:aws:iam::1111111111111111:role/formkiq-stacks-document-unittest-Do-AdminGroupRole-FD83ZGMKR6KP]", - "cognito:username": "test@formkiq.com", - "email": "test@formkiq.com", - "email_verified": "true", - "event_id": "665f0717-4fbc-4511-912b-6cb6f566e1c0", - "exp": "1594269426", - "iat": "1594265826", - "iss": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_3vWU0jnGp", - "sub": "9d7e8352-2142-4363-9a66-45a65daaa0de", - "token_use": "id" - }, - "scopes": null - }, - "domainName": "0jau435s9j.execute-api.us-east-1.amazonaws.com", - "domainPrefix": "0jau435s9j", - "extendedRequestId": "PYuYDiIrIAMEMGA=", - "httpMethod": "GET", - "identity": { - "accessKey": null, - "accountId": null, - "caller": null, - "cognitoAuthenticationProvider": null, - "cognitoAuthenticationType": null, - "cognitoIdentityId": null, - "cognitoIdentityPoolId": null, - "principalOrgId": null, - "sourceIp": "104.246.140.89", - "user": null, - "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:78.0) Gecko/20100101 Firefox/78.0", - "userArn": null - }, - "path": "/sites", - "protocol": "HTTP/1.1", - "requestId": "PYuYDiIrIAMEMGA=", - "requestTime": "09/Jul/2020:03:37:36 +0000", - "requestTimeEpoch": 1594265856216, - "resourceId": "GET /sites", - "resourcePath": "/sites", - "stage": "$default" - }, - "pathParameters": null, - "stageVariables": null, - "body": null, - "isBase64Encoded": false -} \ No newline at end of file diff --git a/lambda-apikey-authorizer/.checkstyle b/lambda-apikey-authorizer/.checkstyle new file mode 100644 index 000000000..2fd0087df --- /dev/null +++ b/lambda-apikey-authorizer/.checkstyle @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/lambda-apikey-authorizer/.classpath b/lambda-apikey-authorizer/.classpath new file mode 100644 index 000000000..66f8050c0 --- /dev/null +++ b/lambda-apikey-authorizer/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lambda-apikey-authorizer/.gitignore b/lambda-apikey-authorizer/.gitignore new file mode 100644 index 000000000..4a95481e6 --- /dev/null +++ b/lambda-apikey-authorizer/.gitignore @@ -0,0 +1,2 @@ +/bin/ +/build/ diff --git a/lambda-apikey-authorizer/.project b/lambda-apikey-authorizer/.project new file mode 100644 index 000000000..a83619eec --- /dev/null +++ b/lambda-apikey-authorizer/.project @@ -0,0 +1,30 @@ + + + lambda-apikey-authorizer + Project core created by Buildship. + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.buildship.core.gradleprojectbuilder + + + + + net.sf.eclipsecs.core.CheckstyleBuilder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.buildship.core.gradleprojectnature + net.sf.eclipsecs.core.CheckstyleNature + + + diff --git a/lambda-apikey-authorizer/.settings/org.eclipse.buildship.core.prefs b/lambda-apikey-authorizer/.settings/org.eclipse.buildship.core.prefs new file mode 100644 index 000000000..e47955840 --- /dev/null +++ b/lambda-apikey-authorizer/.settings/org.eclipse.buildship.core.prefs @@ -0,0 +1,13 @@ +arguments= +auto.sync=false +build.scans.enabled=false +connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) +connection.project.dir= +eclipse.preferences.version=1 +gradle.user.home= +java.home= +jvm.arguments= +offline.mode=false +override.workspace.settings=false +show.console.view=false +show.executions.view=false diff --git a/lambda-apikey-authorizer/.settings/org.eclipse.core.resources.prefs b/lambda-apikey-authorizer/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 000000000..99f26c020 --- /dev/null +++ b/lambda-apikey-authorizer/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/lambda-apikey-authorizer/.settings/org.eclipse.jdt.core.prefs b/lambda-apikey-authorizer/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..9ba0f3224 --- /dev/null +++ b/lambda-apikey-authorizer/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,496 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.codeComplete.argumentPrefixes= +org.eclipse.jdt.core.codeComplete.argumentSuffixes= +org.eclipse.jdt.core.codeComplete.fieldPrefixes= +org.eclipse.jdt.core.codeComplete.fieldSuffixes= +org.eclipse.jdt.core.codeComplete.localPrefixes= +org.eclipse.jdt.core.codeComplete.localSuffixes= +org.eclipse.jdt.core.codeComplete.staticFieldPrefixes= +org.eclipse.jdt.core.codeComplete.staticFieldSuffixes= +org.eclipse.jdt.core.codeComplete.staticFinalFieldPrefixes= +org.eclipse.jdt.core.codeComplete.staticFinalFieldSuffixes= +org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled +org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore +org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull +org.eclipse.jdt.core.compiler.annotation.nonnull.secondary= +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary= +org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable +org.eclipse.jdt.core.compiler.annotation.nullable.secondary= +org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=11 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=11 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.doc.comment.support=enabled +org.eclipse.jdt.core.compiler.problem.APILeak=warning +org.eclipse.jdt.core.compiler.problem.annotatedTypeArgumentToUnannotated=info +org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.autoboxing=warning +org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning +org.eclipse.jdt.core.compiler.problem.deadCode=warning +org.eclipse.jdt.core.compiler.problem.deprecation=warning +org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled +org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled +org.eclipse.jdt.core.compiler.problem.discouragedReference=warning +org.eclipse.jdt.core.compiler.problem.emptyStatement=warning +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore +org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=warning +org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning +org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning +org.eclipse.jdt.core.compiler.problem.forbiddenReference=error +org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning +org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled +org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning +org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning +org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=warning +org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning +org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=disabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=disabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=public +org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled +org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning +org.eclipse.jdt.core.compiler.problem.missingJavadocComments=warning +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public +org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag +org.eclipse.jdt.core.compiler.problem.missingJavadocTags=warning +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=public +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning +org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore +org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning +org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning +org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore +org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning +org.eclipse.jdt.core.compiler.problem.nonnullTypeVariableFromLegacyInvocation=warning +org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error +org.eclipse.jdt.core.compiler.problem.nullReference=warning +org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error +org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=warning +org.eclipse.jdt.core.compiler.problem.pessimisticNullAnalysisForFreeTypeVariables=warning +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning +org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning +org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=warning +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning +org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning +org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.suppressWarningsNotFullyAnalysed=info +org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=warning +org.eclipse.jdt.core.compiler.problem.terminalDeprecation=warning +org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning +org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled +org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning +org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning +org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore +org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning +org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentType=warning +org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentTypeStrict=disabled +org.eclipse.jdt.core.compiler.problem.unlikelyEqualsArgumentType=info +org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=warning +org.eclipse.jdt.core.compiler.problem.unstableAutoModuleName=warning +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled +org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedImport=warning +org.eclipse.jdt.core.compiler.problem.unusedLabel=warning +org.eclipse.jdt.core.compiler.problem.unusedLocal=warning +org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=warning +org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled +org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning +org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.source=11 +org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns=false +org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647 +org.eclipse.jdt.core.formatter.align_type_members_on_columns=false +org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns=false +org.eclipse.jdt.core.formatter.align_with_spaces=false +org.eclipse.jdt.core.formatter.alignment_for_additive_operator=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_assignment=16 +org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_loops=16 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression_chain=0 +org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header=0 +org.eclipse.jdt.core.formatter.alignment_for_logical_operator=16 +org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_module_statements=16 +org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +org.eclipse.jdt.core.formatter.alignment_for_multiplicative_operator=16 +org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references=0 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_relational_operator=0 +org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80 +org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_shift_operator=0 +org.eclipse.jdt.core.formatter.alignment_for_string_concatenation=16 +org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_type_arguments=0 +org.eclipse.jdt.core.formatter.alignment_for_type_parameters=0 +org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16 +org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_after_package=1 +org.eclipse.jdt.core.formatter.blank_lines_before_field=0 +org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 +org.eclipse.jdt.core.formatter.blank_lines_before_imports=0 +org.eclipse.jdt.core.formatter.blank_lines_before_member_type=0 +org.eclipse.jdt.core.formatter.blank_lines_before_method=1 +org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 +org.eclipse.jdt.core.formatter.blank_lines_before_package=0 +org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=0 +org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=2 +org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.comment.align_tags_descriptions_grouped=false +org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions=false +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false +org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position=true +org.eclipse.jdt.core.formatter.comment.format_block_comments=true +org.eclipse.jdt.core.formatter.comment.format_header=true +org.eclipse.jdt.core.formatter.comment.format_html=true +org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true +org.eclipse.jdt.core.formatter.comment.format_line_comments=true +org.eclipse.jdt.core.formatter.comment.format_source_code=true +org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false +org.eclipse.jdt.core.formatter.comment.indent_root_tags=true +org.eclipse.jdt.core.formatter.comment.indent_tag_description=false +org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert +org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert +org.eclipse.jdt.core.formatter.comment.line_length=100 +org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true +org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true +org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false +org.eclipse.jdt.core.formatter.compact_else_if=true +org.eclipse.jdt.core.formatter.continuation_indentation=2 +org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2 +org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off +org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on +org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false +org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true +org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_empty_lines=false +org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true +org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true +org.eclipse.jdt.core.formatter.indentation.size=2 +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert +org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_after_additive_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_bitwise_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert +org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_after_logical_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_multiplicative_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_relational_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert +org.eclipse.jdt.core.formatter.insert_space_after_shift_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_string_concatenation=insert +org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_additive_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_bitwise_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_before_logical_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_multiplicative_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert +org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_relational_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_shift_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation=insert +org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.join_lines_in_comments=true +org.eclipse.jdt.core.formatter.join_wrapped_lines=true +org.eclipse.jdt.core.formatter.keep_annotation_declaration_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_anonymous_type_declaration_on_one_line=one_line_if_empty +org.eclipse.jdt.core.formatter.keep_code_block_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false +org.eclipse.jdt.core.formatter.keep_enum_constant_declaration_on_one_line=one_line_if_empty +org.eclipse.jdt.core.formatter.keep_enum_declaration_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_if_then_body_block_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false +org.eclipse.jdt.core.formatter.keep_lambda_body_block_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_loop_body_block_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_method_body_on_one_line=one_line_if_empty +org.eclipse.jdt.core.formatter.keep_simple_do_while_body_on_same_line=false +org.eclipse.jdt.core.formatter.keep_simple_for_body_on_same_line=false +org.eclipse.jdt.core.formatter.keep_simple_getter_setter_on_one_line=false +org.eclipse.jdt.core.formatter.keep_simple_while_body_on_same_line=false +org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_type_declaration_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.lineSplit=100 +org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false +org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 +org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=3 +org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause=common_lines +org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=false +org.eclipse.jdt.core.formatter.tabulation.char=space +org.eclipse.jdt.core.formatter.tabulation.size=2 +org.eclipse.jdt.core.formatter.use_on_off_tags=true +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false +org.eclipse.jdt.core.formatter.wrap_before_additive_operator=true +org.eclipse.jdt.core.formatter.wrap_before_assignment_operator=false +org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true +org.eclipse.jdt.core.formatter.wrap_before_bitwise_operator=true +org.eclipse.jdt.core.formatter.wrap_before_conditional_operator=true +org.eclipse.jdt.core.formatter.wrap_before_logical_operator=true +org.eclipse.jdt.core.formatter.wrap_before_multiplicative_operator=true +org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true +org.eclipse.jdt.core.formatter.wrap_before_relational_operator=true +org.eclipse.jdt.core.formatter.wrap_before_shift_operator=true +org.eclipse.jdt.core.formatter.wrap_before_string_concatenation=true +org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true +org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter diff --git a/lambda-apikey-authorizer/.settings/org.eclipse.jdt.ui.prefs b/lambda-apikey-authorizer/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 000000000..c13d929fd --- /dev/null +++ b/lambda-apikey-authorizer/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,8 @@ +eclipse.preferences.version=1 +formatter_profile=_GoogleStyle +formatter_settings_version=16 +org.eclipse.jdt.ui.exception.name=e +org.eclipse.jdt.ui.gettersetter.use.is=true +org.eclipse.jdt.ui.keywordthis=true +org.eclipse.jdt.ui.overrideannotation=true +org.eclipse.jdt.ui.text.custom_code_templates= diff --git a/lambda-apikey-authorizer/build.gradle b/lambda-apikey-authorizer/build.gradle new file mode 100644 index 000000000..a02f08731 --- /dev/null +++ b/lambda-apikey-authorizer/build.gradle @@ -0,0 +1,80 @@ +description = "FormKiQ Core - API Key Authorizer" + +def moduleName = "formkiq-module-lambda-authorizer-apikey" + +dependencies { + + annotationProcessor group: 'com.formkiq', name: 'graalvm-annotations-processor', version: '1.4.0' + + implementation project(':aws-dynamodb') + implementation project(':dynamodb-documents') + implementation project(':fkq-lambda-services') + + implementation group: 'org.slf4j', name: 'slf4j-simple', version: '2.0.7' + implementation group: 'com.amazonaws', name: 'aws-lambda-java-core', version: '1.2.2' + implementation group: 'com.google.code.gson', name: 'gson', version: '2.10.1' + + implementation group: 'com.formkiq', name: 'lambda-runtime-graalvm', version:'2.3.1' + implementation group: 'com.formkiq', name: 'graalvm-annotations', version: '1.2.0' + + testImplementation project(':fkq-test-utils') + + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version:'5.9.1' + testImplementation group: 'org.testcontainers', name: 'testcontainers', version: '1.17.6' + testImplementation group: 'org.testcontainers', name: 'junit-jupiter', version: '1.17.6' +} + +compileJava { + options.annotationProcessorPath += configurations.runtimeClasspath +} + +nativeImage { + outputFileName = "server" + mainClassName = "com.formkiq.lambda.runtime.graalvm.LambdaRuntime" + dockerImage = "ghcr.io/graalvm/graalvm-ce:ol9-java17-22.3.1" + enableHttp = true + enableHttps = true + enableStatic = true + systemProperty = ["java.net.preferIPv4Stack=true"] +} + +test { + failFast = true + useJUnitPlatform() +} + +task buildZip(type: Zip) { + dependsOn test, graalvmNativeImage + inputs.files("${project.projectDir}/runtime/bootstrap", "${buildDir}/graalvm/server") + outputs.file("${buildDir}/${moduleName}.zip") + + archiveFileName = "${moduleName}.zip" + destinationDirectory = file("${buildDir}") + from("${project.projectDir}/runtime") { + include 'bootstrap' + } + from("${buildDir}/graalvm") { + include 'server' + } +} + +task buildJava11Zip(type: Zip) { + from compileJava + into('lib') { + from configurations.default + } +} + +task assembleTemplate { + dependsOn buildZip + outputs.upToDateWhen { false } + + doLast { + copy { + from layout.projectDirectory.file("${buildDir}/${moduleName}.zip") + into "${buildDir}/distributions/formkiq-core/sam/api" + } + } +} + +build.dependsOn assembleTemplate diff --git a/lambda-apikey-authorizer/config/checkstyle/checkstyle.xml b/lambda-apikey-authorizer/config/checkstyle/checkstyle.xml new file mode 100644 index 000000000..175b5d8a0 --- /dev/null +++ b/lambda-apikey-authorizer/config/checkstyle/checkstyle.xml @@ -0,0 +1,243 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lambda-apikey-authorizer/config/checkstyle/import-control.xml b/lambda-apikey-authorizer/config/checkstyle/import-control.xml new file mode 100644 index 000000000..99a05ed12 --- /dev/null +++ b/lambda-apikey-authorizer/config/checkstyle/import-control.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lambda-apikey-authorizer/config/checkstyle/mysuppressions.xml b/lambda-apikey-authorizer/config/checkstyle/mysuppressions.xml new file mode 100644 index 000000000..b5bb10162 --- /dev/null +++ b/lambda-apikey-authorizer/config/checkstyle/mysuppressions.xml @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/lambda-apikey-authorizer/runtime/bootstrap b/lambda-apikey-authorizer/runtime/bootstrap new file mode 100755 index 000000000..8cb383935 --- /dev/null +++ b/lambda-apikey-authorizer/runtime/bootstrap @@ -0,0 +1,3 @@ +#!/bin/sh +set -euo pipefail +./server diff --git a/lambda-apikey-authorizer/src/main/java/com/formkiq/module/lambda/authorizer/apikey/ApiKeyAuthorizerRequestHandler.java b/lambda-apikey-authorizer/src/main/java/com/formkiq/module/lambda/authorizer/apikey/ApiKeyAuthorizerRequestHandler.java new file mode 100644 index 000000000..00cfbbd5c --- /dev/null +++ b/lambda-apikey-authorizer/src/main/java/com/formkiq/module/lambda/authorizer/apikey/ApiKeyAuthorizerRequestHandler.java @@ -0,0 +1,153 @@ +/** + * MIT License + * + * Copyright (c) 2018 - 2020 FormKiQ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.formkiq.module.lambda.authorizer.apikey; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.LambdaLogger; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.RequestStreamHandler; +import com.formkiq.aws.dynamodb.DynamicObject; +import com.formkiq.aws.dynamodb.DynamoDbConnectionBuilder; +import com.formkiq.aws.dynamodb.DynamoDbConnectionBuilderExtension; +import com.formkiq.graalvm.annotations.Reflectable; +import com.formkiq.module.lambdaservices.AwsServiceCache; +import com.formkiq.stacks.dynamodb.ApiKeysService; +import com.formkiq.stacks.dynamodb.ApiKeysServiceExtension; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.utils.IoUtils; + +/** {@link RequestHandler} for handling DynamoDb to Tesseract OCR Processor. */ +@Reflectable +public class ApiKeyAuthorizerRequestHandler implements RequestStreamHandler { + + /** {@link AwsServiceCache}. */ + private AwsServiceCache awsServices; + /** {@link Gson}. */ + private Gson gson = new GsonBuilder().disableHtmlEscaping().create(); + + /** + * constructor. + * + */ + public ApiKeyAuthorizerRequestHandler() { + this(System.getenv(), + new DynamoDbConnectionBuilder("true".equals(System.getenv("ENABLE_AWS_X_RAY"))) + .setRegion(Region.of(System.getenv("AWS_REGION")))); + } + + /** + * constructor. + * + * @param map {@link Map} + * @param dbConnection {@link DynamoDbConnectionBuilder} + */ + public ApiKeyAuthorizerRequestHandler(final Map map, + final DynamoDbConnectionBuilder dbConnection) { + + this.awsServices = + new AwsServiceCache().environment(map).debug("true".equals(map.get("DEBUG"))); + + AwsServiceCache.register(DynamoDbConnectionBuilder.class, + new DynamoDbConnectionBuilderExtension(dbConnection)); + AwsServiceCache.register(ApiKeysService.class, new ApiKeysServiceExtension()); + } + + /** + * Get {@link AwsServiceCache}. + * + * @return {@link AwsServiceCache} + */ + public AwsServiceCache getAwsServices() { + return this.awsServices; + } + + @SuppressWarnings("unchecked") + private String getIdentitySource(final Map map) { + List identitySource = (List) map.get("identitySource"); + return !identitySource.isEmpty() ? identitySource.get(0) : null; + } + + @SuppressWarnings("unchecked") + @Override + public void handleRequest(final InputStream input, final OutputStream output, + final Context context) throws IOException { + + LambdaLogger logger = context.getLogger(); + ApiKeysService apiKeys = this.awsServices.getExtension(ApiKeysService.class); + + String json = IoUtils.toUtf8String(input); + + if (this.awsServices.debug()) { + logger.log(json); + } + + Map map = this.gson.fromJson(json, Map.class); + + String apiKey = getIdentitySource(map); + + DynamicObject obj = apiKeys.get(apiKey); + List siteIds = obj.getStringList("siteIds"); + boolean isAuthorized = apiKey != null && apiKey.equals(obj.getOrDefault("apiKey", "")); + + String apiKeyName = (String) obj.getOrDefault("name", ""); + String group = isAuthorized ? "[" + String.join(",", siteIds) + "]" : "[]"; + + log(logger, map, isAuthorized, group); + Map response = Map.of("isAuthorized", Boolean.valueOf(isAuthorized), "context", + Map.of("apiKeyClaims", Map.of("cognito:groups", group, "cognito:username", apiKeyName))); + + OutputStreamWriter writer = new OutputStreamWriter(output, "UTF-8"); + writer.write(this.gson.toJson(response)); + writer.close(); + } + + @SuppressWarnings("unchecked") + private void log(final LambdaLogger logger, final Map map, + final boolean isAuthorized, final String group) { + + Map requestContext = + map.containsKey("requestContext") ? (Map) map.get("requestContext") + : Collections.emptyMap(); + Map http = + map.containsKey("http") ? (Map) map.get("http") : Collections.emptyMap(); + + String s = String.format( + "{\"requestId\": \"%s\",\"ip\": \"%s\",\"requestTime\": \"%s\",\"httpMethod\": \"%s\"," + + "\"routeKey\": \"%s\"," + + "\"protocol\": \"%s\",\"siteId\":\"%s\",\"isAuthorized\":\"%s\"}", + map.get("requestId"), http.get("sourceIp"), requestContext.get("time"), http.get("method"), + map.get("routeKey"), requestContext.get("protocol"), group, String.valueOf(isAuthorized)); + + logger.log(s); + } +} diff --git a/lambda-apikey-authorizer/src/test/java/com/formkiq/module/lambda/authorizer/apikey/ApiKeyAuthorizerRequestHandlerTest.java b/lambda-apikey-authorizer/src/test/java/com/formkiq/module/lambda/authorizer/apikey/ApiKeyAuthorizerRequestHandlerTest.java new file mode 100644 index 000000000..4d7437310 --- /dev/null +++ b/lambda-apikey-authorizer/src/test/java/com/formkiq/module/lambda/authorizer/apikey/ApiKeyAuthorizerRequestHandlerTest.java @@ -0,0 +1,185 @@ +/** + * MIT License + * + * Copyright (c) 2018 - 2020 FormKiQ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.formkiq.module.lambda.authorizer.apikey; + +import static com.formkiq.testutils.aws.DynamoDbExtension.DOCUMENTS_TABLE; +import static org.junit.jupiter.api.Assertions.assertEquals; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import com.amazonaws.services.lambda.runtime.Context; +import com.formkiq.aws.dynamodb.DynamoDbConnectionBuilder; +import com.formkiq.module.lambdaservices.AwsServiceCache; +import com.formkiq.stacks.dynamodb.ApiKeysService; +import com.formkiq.testutils.aws.DynamoDbExtension; +import com.formkiq.testutils.aws.DynamoDbTestServices; +import com.formkiq.testutils.aws.LambdaContextRecorder; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +/** + * + * Unit Tests {@link ApiKeyAuthorizerRequestHandler}. + * + */ +@ExtendWith(DynamoDbExtension.class) +class ApiKeyAuthorizerRequestHandlerTest { + + /** {@link ApiKeysService}. */ + private static ApiKeysService apiKeysService; + /** {@link Gson}. */ + private static final Gson GSON = new GsonBuilder().create(); + /** {@link ApiKeyAuthorizerRequestHandler}. */ + private static ApiKeyAuthorizerRequestHandler processor; + + @BeforeAll + public static void beforeAll() throws Exception { + + DynamoDbConnectionBuilder dbConnection = DynamoDbTestServices.getDynamoDbConnection(); + + processor = new ApiKeyAuthorizerRequestHandler(Map.of("DOCUMENTS_TABLE", DOCUMENTS_TABLE), + dbConnection); + + AwsServiceCache awsServices = processor.getAwsServices(); + apiKeysService = awsServices.getExtension(ApiKeysService.class); + } + + /** {@link Context}. */ + private Context context = new LambdaContextRecorder(); + + private InputStream getInput(final String apiKey) { + List identitySource = apiKey != null ? Arrays.asList(apiKey) : Collections.emptyList(); + String json = GSON.toJson(Map.of("type", "REQUEST", "identitySource", identitySource)); + + InputStream is = new ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8)); + return is; + } + + /** + * Test Invalid API Key. + * + * @throws Exception Exception + */ + @SuppressWarnings("unchecked") + @Test + void testHandleRequest01() throws Exception { + // given + String apiKey = UUID.randomUUID().toString(); + + try (InputStream is = getInput(apiKey)) { + + ByteArrayOutputStream os = new ByteArrayOutputStream(); + + // when + processor.handleRequest(is, os, this.context); + + // then + String response = new String(os.toByteArray(), "UTF-8"); + Map map = GSON.fromJson(response, Map.class); + assertEquals(Boolean.FALSE, map.get("isAuthorized")); + Map ctx = (Map) map.get("context"); + Map claims = (Map) ctx.get("apiKeyClaims"); + assertEquals("[]", claims.get("cognito:groups")); + assertEquals("", claims.get("cognito:username")); + } + } + + /** + * Test VALID API Key. + * + * @throws Exception Exception + */ + @SuppressWarnings("unchecked") + @Test + void testHandleRequest02() throws Exception { + // given + for (String siteId : Arrays.asList(null, UUID.randomUUID().toString())) { + + String name = UUID.randomUUID().toString(); + + String apiKey = apiKeysService.createApiKey(siteId, name, "joe"); + + try (InputStream is = getInput(apiKey)) { + + ByteArrayOutputStream os = new ByteArrayOutputStream(); + + // when + processor.handleRequest(is, os, this.context); + + // then + String response = new String(os.toByteArray(), "UTF-8"); + Map map = GSON.fromJson(response, Map.class); + assertEquals(Boolean.TRUE, map.get("isAuthorized")); + + Map ctx = (Map) map.get("context"); + Map claims = (Map) ctx.get("apiKeyClaims"); + + if (siteId != null) { + assertEquals("[" + siteId + "]", claims.get("cognito:groups")); + } else { + assertEquals("[default]", claims.get("cognito:groups")); + } + + assertEquals(name, claims.get("cognito:username")); + } + } + } + + /** + * Test missing API Key. + * + * @throws Exception Exception + */ + @SuppressWarnings("unchecked") + @Test + void testHandleRequest03() throws Exception { + // given + try (InputStream is = getInput(null)) { + + ByteArrayOutputStream os = new ByteArrayOutputStream(); + + // when + processor.handleRequest(is, os, this.context); + + // then + String response = new String(os.toByteArray(), "UTF-8"); + Map map = GSON.fromJson(response, Map.class); + assertEquals(Boolean.FALSE, map.get("isAuthorized")); + + Map ctx = (Map) map.get("context"); + Map claims = (Map) ctx.get("apiKeyClaims"); + assertEquals("[]", claims.get("cognito:groups")); + assertEquals("", claims.get("cognito:username")); + } + } +} diff --git a/lambda-ocr-tesseract/.checkstyle b/lambda-ocr-tesseract/.checkstyle new file mode 100644 index 000000000..f9b8404e0 --- /dev/null +++ b/lambda-ocr-tesseract/.checkstyle @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/lambda-ocr-tesseract/.classpath b/lambda-ocr-tesseract/.classpath new file mode 100644 index 000000000..66f8050c0 --- /dev/null +++ b/lambda-ocr-tesseract/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lambda-ocr-tesseract/.gitignore b/lambda-ocr-tesseract/.gitignore new file mode 100644 index 000000000..4a95481e6 --- /dev/null +++ b/lambda-ocr-tesseract/.gitignore @@ -0,0 +1,2 @@ +/bin/ +/build/ diff --git a/lambda-ocr-tesseract/.project b/lambda-ocr-tesseract/.project new file mode 100644 index 000000000..522c18c99 --- /dev/null +++ b/lambda-ocr-tesseract/.project @@ -0,0 +1,30 @@ + + + lambda-ocr-tesseract + Project core created by Buildship. + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.buildship.core.gradleprojectbuilder + + + + + net.sf.eclipsecs.core.CheckstyleBuilder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.buildship.core.gradleprojectnature + net.sf.eclipsecs.core.CheckstyleNature + + + diff --git a/lambda-ocr-tesseract/.settings/org.eclipse.buildship.core.prefs b/lambda-ocr-tesseract/.settings/org.eclipse.buildship.core.prefs new file mode 100644 index 000000000..e47955840 --- /dev/null +++ b/lambda-ocr-tesseract/.settings/org.eclipse.buildship.core.prefs @@ -0,0 +1,13 @@ +arguments= +auto.sync=false +build.scans.enabled=false +connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) +connection.project.dir= +eclipse.preferences.version=1 +gradle.user.home= +java.home= +jvm.arguments= +offline.mode=false +override.workspace.settings=false +show.console.view=false +show.executions.view=false diff --git a/lambda-ocr-tesseract/.settings/org.eclipse.core.resources.prefs b/lambda-ocr-tesseract/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 000000000..99f26c020 --- /dev/null +++ b/lambda-ocr-tesseract/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/lambda-ocr-tesseract/.settings/org.eclipse.jdt.core.prefs b/lambda-ocr-tesseract/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..9ba0f3224 --- /dev/null +++ b/lambda-ocr-tesseract/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,496 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.codeComplete.argumentPrefixes= +org.eclipse.jdt.core.codeComplete.argumentSuffixes= +org.eclipse.jdt.core.codeComplete.fieldPrefixes= +org.eclipse.jdt.core.codeComplete.fieldSuffixes= +org.eclipse.jdt.core.codeComplete.localPrefixes= +org.eclipse.jdt.core.codeComplete.localSuffixes= +org.eclipse.jdt.core.codeComplete.staticFieldPrefixes= +org.eclipse.jdt.core.codeComplete.staticFieldSuffixes= +org.eclipse.jdt.core.codeComplete.staticFinalFieldPrefixes= +org.eclipse.jdt.core.codeComplete.staticFinalFieldSuffixes= +org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled +org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore +org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull +org.eclipse.jdt.core.compiler.annotation.nonnull.secondary= +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary= +org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable +org.eclipse.jdt.core.compiler.annotation.nullable.secondary= +org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=11 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=11 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.doc.comment.support=enabled +org.eclipse.jdt.core.compiler.problem.APILeak=warning +org.eclipse.jdt.core.compiler.problem.annotatedTypeArgumentToUnannotated=info +org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.autoboxing=warning +org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning +org.eclipse.jdt.core.compiler.problem.deadCode=warning +org.eclipse.jdt.core.compiler.problem.deprecation=warning +org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled +org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled +org.eclipse.jdt.core.compiler.problem.discouragedReference=warning +org.eclipse.jdt.core.compiler.problem.emptyStatement=warning +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore +org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=warning +org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning +org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning +org.eclipse.jdt.core.compiler.problem.forbiddenReference=error +org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning +org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled +org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning +org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning +org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=warning +org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning +org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=disabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=disabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=public +org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled +org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning +org.eclipse.jdt.core.compiler.problem.missingJavadocComments=warning +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public +org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag +org.eclipse.jdt.core.compiler.problem.missingJavadocTags=warning +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=public +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning +org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore +org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning +org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning +org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore +org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning +org.eclipse.jdt.core.compiler.problem.nonnullTypeVariableFromLegacyInvocation=warning +org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error +org.eclipse.jdt.core.compiler.problem.nullReference=warning +org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error +org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=warning +org.eclipse.jdt.core.compiler.problem.pessimisticNullAnalysisForFreeTypeVariables=warning +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning +org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning +org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=warning +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning +org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning +org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.suppressWarningsNotFullyAnalysed=info +org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=warning +org.eclipse.jdt.core.compiler.problem.terminalDeprecation=warning +org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning +org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled +org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning +org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning +org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore +org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning +org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentType=warning +org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentTypeStrict=disabled +org.eclipse.jdt.core.compiler.problem.unlikelyEqualsArgumentType=info +org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=warning +org.eclipse.jdt.core.compiler.problem.unstableAutoModuleName=warning +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled +org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedImport=warning +org.eclipse.jdt.core.compiler.problem.unusedLabel=warning +org.eclipse.jdt.core.compiler.problem.unusedLocal=warning +org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=warning +org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled +org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning +org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.source=11 +org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns=false +org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647 +org.eclipse.jdt.core.formatter.align_type_members_on_columns=false +org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns=false +org.eclipse.jdt.core.formatter.align_with_spaces=false +org.eclipse.jdt.core.formatter.alignment_for_additive_operator=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_assignment=16 +org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_loops=16 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression_chain=0 +org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header=0 +org.eclipse.jdt.core.formatter.alignment_for_logical_operator=16 +org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_module_statements=16 +org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +org.eclipse.jdt.core.formatter.alignment_for_multiplicative_operator=16 +org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references=0 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_relational_operator=0 +org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80 +org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_shift_operator=0 +org.eclipse.jdt.core.formatter.alignment_for_string_concatenation=16 +org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_type_arguments=0 +org.eclipse.jdt.core.formatter.alignment_for_type_parameters=0 +org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16 +org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_after_package=1 +org.eclipse.jdt.core.formatter.blank_lines_before_field=0 +org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 +org.eclipse.jdt.core.formatter.blank_lines_before_imports=0 +org.eclipse.jdt.core.formatter.blank_lines_before_member_type=0 +org.eclipse.jdt.core.formatter.blank_lines_before_method=1 +org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 +org.eclipse.jdt.core.formatter.blank_lines_before_package=0 +org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=0 +org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=2 +org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.comment.align_tags_descriptions_grouped=false +org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions=false +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false +org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position=true +org.eclipse.jdt.core.formatter.comment.format_block_comments=true +org.eclipse.jdt.core.formatter.comment.format_header=true +org.eclipse.jdt.core.formatter.comment.format_html=true +org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true +org.eclipse.jdt.core.formatter.comment.format_line_comments=true +org.eclipse.jdt.core.formatter.comment.format_source_code=true +org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false +org.eclipse.jdt.core.formatter.comment.indent_root_tags=true +org.eclipse.jdt.core.formatter.comment.indent_tag_description=false +org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert +org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert +org.eclipse.jdt.core.formatter.comment.line_length=100 +org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true +org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true +org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false +org.eclipse.jdt.core.formatter.compact_else_if=true +org.eclipse.jdt.core.formatter.continuation_indentation=2 +org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2 +org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off +org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on +org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false +org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true +org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_empty_lines=false +org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true +org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true +org.eclipse.jdt.core.formatter.indentation.size=2 +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert +org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_after_additive_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_bitwise_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert +org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_after_logical_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_multiplicative_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_relational_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert +org.eclipse.jdt.core.formatter.insert_space_after_shift_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_string_concatenation=insert +org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_additive_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_bitwise_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_before_logical_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_multiplicative_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert +org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_relational_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_shift_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation=insert +org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.join_lines_in_comments=true +org.eclipse.jdt.core.formatter.join_wrapped_lines=true +org.eclipse.jdt.core.formatter.keep_annotation_declaration_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_anonymous_type_declaration_on_one_line=one_line_if_empty +org.eclipse.jdt.core.formatter.keep_code_block_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false +org.eclipse.jdt.core.formatter.keep_enum_constant_declaration_on_one_line=one_line_if_empty +org.eclipse.jdt.core.formatter.keep_enum_declaration_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_if_then_body_block_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false +org.eclipse.jdt.core.formatter.keep_lambda_body_block_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_loop_body_block_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_method_body_on_one_line=one_line_if_empty +org.eclipse.jdt.core.formatter.keep_simple_do_while_body_on_same_line=false +org.eclipse.jdt.core.formatter.keep_simple_for_body_on_same_line=false +org.eclipse.jdt.core.formatter.keep_simple_getter_setter_on_one_line=false +org.eclipse.jdt.core.formatter.keep_simple_while_body_on_same_line=false +org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_type_declaration_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.lineSplit=100 +org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false +org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 +org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=3 +org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause=common_lines +org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=false +org.eclipse.jdt.core.formatter.tabulation.char=space +org.eclipse.jdt.core.formatter.tabulation.size=2 +org.eclipse.jdt.core.formatter.use_on_off_tags=true +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false +org.eclipse.jdt.core.formatter.wrap_before_additive_operator=true +org.eclipse.jdt.core.formatter.wrap_before_assignment_operator=false +org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true +org.eclipse.jdt.core.formatter.wrap_before_bitwise_operator=true +org.eclipse.jdt.core.formatter.wrap_before_conditional_operator=true +org.eclipse.jdt.core.formatter.wrap_before_logical_operator=true +org.eclipse.jdt.core.formatter.wrap_before_multiplicative_operator=true +org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true +org.eclipse.jdt.core.formatter.wrap_before_relational_operator=true +org.eclipse.jdt.core.formatter.wrap_before_shift_operator=true +org.eclipse.jdt.core.formatter.wrap_before_string_concatenation=true +org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true +org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter diff --git a/lambda-ocr-tesseract/.settings/org.eclipse.jdt.ui.prefs b/lambda-ocr-tesseract/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 000000000..c13d929fd --- /dev/null +++ b/lambda-ocr-tesseract/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,8 @@ +eclipse.preferences.version=1 +formatter_profile=_GoogleStyle +formatter_settings_version=16 +org.eclipse.jdt.ui.exception.name=e +org.eclipse.jdt.ui.gettersetter.use.is=true +org.eclipse.jdt.ui.keywordthis=true +org.eclipse.jdt.ui.overrideannotation=true +org.eclipse.jdt.ui.text.custom_code_templates= diff --git a/lambda-ocr-tesseract/build.gradle b/lambda-ocr-tesseract/build.gradle new file mode 100644 index 000000000..a4e8ea577 --- /dev/null +++ b/lambda-ocr-tesseract/build.gradle @@ -0,0 +1,99 @@ +description = "FormKiQ Core - Lambda OCR Tesseract" + +def tesseract_version = "5.3.1" +def moduleName = "formkiq-module-lambda-ocr-tesseract" + +dependencies { + + implementation project(':actions') + implementation project(':aws-s3') + implementation project(':aws-sqs') + implementation project(':aws-sns') + implementation project(':aws-dynamodb') + implementation project(':ocr') + implementation project(':fkq-lambda-services') + implementation("net.sourceforge.tess4j:tess4j:5.7.0") + implementation("org.slf4j:slf4j-simple:2.0.6") + implementation group: 'com.amazonaws', name: 'aws-lambda-java-core', version: '1.2.2' + implementation group: 'com.google.code.gson', name: 'gson', version: '2.10.1' + implementation group: 'net.java.dev.jna', name: 'jna', version: '5.13.0' + + + testImplementation project(':fkq-test-utils') + + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version:'5.9.1' + testImplementation group: 'org.testcontainers', name: 'testcontainers', version: '1.17.6' + testImplementation group: 'org.testcontainers', name: 'junit-jupiter', version: '1.17.6' + testImplementation group: 'org.testcontainers', name: 'localstack', version: '1.17.6' +} + +task buildZip(type: Zip) { + dependsOn check + archiveFileName = "${moduleName}.zip" + destinationDirectory = file("${buildDir}") + from compileJava + from processResources + into('lib') { + from configurations.runtimeClasspath + } +} + +test { + failFast = true + useJUnitPlatform() +} + +task downloadZipFile(type: Download) { + src 'https://github.com/bweigel/aws-lambda-tesseract-layer/releases/download/v5.3.1/tesseract-al2-x86.zip' + dest new File(buildDir, 'tesseract-al2-x86.zip') + overwrite false +} + +task downloadAndUnzipFile(dependsOn: downloadZipFile, type: Copy) { + from zipTree(downloadZipFile.dest) + into new File(buildDir, 'tesseract') +} + +task updateTesseractFiles(type: Delete) { + dependsOn downloadAndUnzipFile + def directory = file("${buildDir}/tesseract/tesseract/share/tessdata/") + + doLast { + file("${buildDir}/tesseract/lib/libtesseract.so.5").renameTo(file("${buildDir}/tesseract/lib/libtesseract.so")) + directory.eachFile { file -> + if (file.name != "eng.traineddata" && file.name != "osd.traineddata") { + file.delete() + } + } + } +} + +task buildTesseractLayer(type: Zip) { + dependsOn updateTesseractFiles + from "${buildDir}/tesseract" + archiveFileName.set("layer-tesseract-5.3.1.zip") + destinationDir file("$buildDir") +} + +task assembleTemplate { + dependsOn buildZip, buildTesseractLayer + outputs.upToDateWhen { false } + + doLast { + copy { + from layout.buildDirectory.dir("${buildDir}/../src/main/resources/cloudformation") + include "template*" + into "${buildDir}/distributions/formkiq-core/sam/ocr-tesseract" + } + copy { + from layout.projectDirectory.file("${buildDir}/${moduleName}.zip") + into "${buildDir}/distributions/formkiq-core/sam/ocr-tesseract" + } + copy { + from layout.projectDirectory.file("${buildDir}/layer-tesseract-${tesseract_version}.zip") + into "${buildDir}/distributions/formkiq-core/sam/ocr-tesseract" + } + } +} + +build.dependsOn assembleTemplate diff --git a/lambda-ocr-tesseract/config/checkstyle/checkstyle.xml b/lambda-ocr-tesseract/config/checkstyle/checkstyle.xml new file mode 100644 index 000000000..175b5d8a0 --- /dev/null +++ b/lambda-ocr-tesseract/config/checkstyle/checkstyle.xml @@ -0,0 +1,243 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lambda-ocr-tesseract/config/checkstyle/import-control.xml b/lambda-ocr-tesseract/config/checkstyle/import-control.xml new file mode 100644 index 000000000..c21c438d0 --- /dev/null +++ b/lambda-ocr-tesseract/config/checkstyle/import-control.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lambda-ocr-tesseract/config/checkstyle/mysuppressions.xml b/lambda-ocr-tesseract/config/checkstyle/mysuppressions.xml new file mode 100644 index 000000000..b5bb10162 --- /dev/null +++ b/lambda-ocr-tesseract/config/checkstyle/mysuppressions.xml @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/lambda-ocr-tesseract/src/main/java/com/formkiq/module/lambda/ocr/tesseract/OcrTesseractProcessor.java b/lambda-ocr-tesseract/src/main/java/com/formkiq/module/lambda/ocr/tesseract/OcrTesseractProcessor.java new file mode 100644 index 000000000..00f224488 --- /dev/null +++ b/lambda-ocr-tesseract/src/main/java/com/formkiq/module/lambda/ocr/tesseract/OcrTesseractProcessor.java @@ -0,0 +1,234 @@ +/** + * MIT License + * + * Copyright (c) 2018 - 2020 FormKiQ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.formkiq.module.lambda.ocr.tesseract; + +import static com.formkiq.aws.dynamodb.SiteIdKeyGenerator.createS3Key; +import static com.formkiq.aws.dynamodb.objects.MimeType.MIME_BMP; +import static com.formkiq.aws.dynamodb.objects.MimeType.MIME_GIF; +import static com.formkiq.aws.dynamodb.objects.MimeType.MIME_JPEG; +import static com.formkiq.aws.dynamodb.objects.MimeType.MIME_JPG; +import static com.formkiq.aws.dynamodb.objects.MimeType.MIME_PNG; +import static com.formkiq.aws.dynamodb.objects.MimeType.MIME_TIF; +import static com.formkiq.aws.dynamodb.objects.MimeType.MIME_TIFF; +import static com.formkiq.aws.dynamodb.objects.MimeType.MIME_WEBP; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.LambdaLogger; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.RequestStreamHandler; +import com.formkiq.aws.dynamodb.DynamoDbConnectionBuilder; +import com.formkiq.aws.dynamodb.DynamoDbConnectionBuilderExtension; +import com.formkiq.aws.dynamodb.objects.MimeType; +import com.formkiq.aws.s3.S3ConnectionBuilder; +import com.formkiq.aws.s3.S3Service; +import com.formkiq.aws.s3.S3ServiceExtension; +import com.formkiq.aws.sns.SnsConnectionBuilder; +import com.formkiq.aws.sqs.SqsMessageRecords; +import com.formkiq.module.actions.ActionStatus; +import com.formkiq.module.actions.ActionType; +import com.formkiq.module.actions.services.ActionsNotificationService; +import com.formkiq.module.actions.services.ActionsNotificationServiceExtension; +import com.formkiq.module.actions.services.ActionsService; +import com.formkiq.module.actions.services.ActionsServiceExtension; +import com.formkiq.module.lambdaservices.AwsServiceCache; +import com.formkiq.module.ocr.DocumentOcrService; +import com.formkiq.module.ocr.DocumentOcrServiceExtension; +import com.formkiq.module.ocr.OcrScanStatus; +import com.formkiq.module.ocr.OcrSqsMessage; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import net.sourceforge.tess4j.TesseractException; +import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.utils.IoUtils; + +/** {@link RequestHandler} for handling DynamoDb to Tesseract OCR Processor. */ +public class OcrTesseractProcessor implements RequestStreamHandler { + + /** Supported Mime Type. */ + private static final List SUPPORTED = Arrays.asList(MIME_PNG, MIME_JPEG, MIME_JPG, + MIME_TIF, MIME_TIFF, MIME_GIF, MIME_WEBP, MIME_BMP); + /** {@link AwsServiceCache}. */ + private AwsServiceCache awsServices; + /** Documents S3 Bucket. */ + private String documentsBucket; + /** {@link Gson}. */ + private Gson gson = new GsonBuilder().create(); + /** {@link String}. */ + private String ocrDocumentsBucket; + /** {@link S3Service}. */ + private S3Service s3Service; + /** {@link TesseractWrapper}. */ + private TesseractWrapper tesseract; + + /** + * constructor. + * + */ + public OcrTesseractProcessor() { + this(System.getenv(), + new DynamoDbConnectionBuilder("true".equals(System.getenv("ENABLE_AWS_X_RAY"))) + .setRegion(Region.of(System.getenv("AWS_REGION"))), + new S3ConnectionBuilder("true".equals(System.getenv("ENABLE_AWS_X_RAY"))) + .setRegion(Region.of(System.getenv("AWS_REGION"))), + new SnsConnectionBuilder("true".equals(System.getenv("ENABLE_AWS_X_RAY"))) + .setCredentials(EnvironmentVariableCredentialsProvider.create()) + .setRegion(Region.of(System.getenv("AWS_REGION"))), + new TesseractWrapperImpl()); + } + + /** + * constructor. + * + * @param map {@link Map} + * @param dbConnection {@link DynamoDbConnectionBuilder} + * @param s3Connection {@link S3ConnectionBuilder} + * @param snsConnection {@link SnsConnectionBuilder} + * @param tesseractWrapper {@link TesseractWrapper} + */ + public OcrTesseractProcessor(final Map map, + final DynamoDbConnectionBuilder dbConnection, final S3ConnectionBuilder s3Connection, + final SnsConnectionBuilder snsConnection, final TesseractWrapper tesseractWrapper) { + + this.s3Service = new S3Service(s3Connection); + this.documentsBucket = map.get("DOCUMENTS_S3_BUCKET"); + this.ocrDocumentsBucket = map.get("OCR_S3_BUCKET"); + this.tesseract = tesseractWrapper; + + this.awsServices = + new AwsServiceCache().environment(map).debug("true".equals(map.get("DEBUG"))); + + AwsServiceCache.register(DynamoDbConnectionBuilder.class, + new DynamoDbConnectionBuilderExtension(dbConnection)); + AwsServiceCache.register(S3Service.class, new S3ServiceExtension(s3Connection)); + AwsServiceCache.register(DocumentOcrService.class, new DocumentOcrServiceExtension()); + AwsServiceCache.register(ActionsService.class, new ActionsServiceExtension()); + + AwsServiceCache.register(ActionsNotificationService.class, + new ActionsNotificationServiceExtension(snsConnection)); + } + + /** + * Get {@link AwsServiceCache}. + * + * @return {@link AwsServiceCache} + */ + public AwsServiceCache getAwsServices() { + return this.awsServices; + } + + @Override + public void handleRequest(final InputStream input, final OutputStream output, + final Context context) throws IOException { + + LambdaLogger logger = context.getLogger(); + + String json = IoUtils.toUtf8String(input); + + if (this.awsServices.debug()) { + logger.log(json); + } + + SqsMessageRecords records = this.gson.fromJson(json, SqsMessageRecords.class); + + DocumentOcrService ocrService = this.awsServices.getExtension(DocumentOcrService.class); + + records.records().forEach(record -> { + + OcrSqsMessage sqsMessage = this.gson.fromJson(record.body(), OcrSqsMessage.class); + processRecord(logger, ocrService, sqsMessage); + + }); + } + + private void processRecord(final LambdaLogger logger, final DocumentOcrService ocrService, + final OcrSqsMessage sqsMessage) { + String siteId = sqsMessage.siteId(); + String documentId = sqsMessage.documentId(); + String jobId = sqsMessage.jobId(); + String contentType = sqsMessage.contentType(); + + String s = String.format( + "{\"siteId\": \"%s\",\"documentId\": \"%s\",\"jobId\": \"%s\",\"contentType\":\"%s\"}", + siteId, documentId, jobId, contentType); + logger.log(s); + + + try { + + MimeType mt = MimeType.fromContentType(contentType); + + if (!SUPPORTED.contains(mt)) { + throw new IOException("unsupported Content-Type: " + contentType); + } + + String tmpDirectory = + new File("/tmp").exists() ? "/tmp/" : System.getProperty("java.io.tmpdir") + "\\"; + String documentS3Key = createS3Key(siteId, documentId); + File file = + new File(tmpDirectory + documentS3Key.replaceAll("/", "_") + "." + mt.getExtension()); + + try (InputStream is = + this.s3Service.getContentAsInputStream(this.documentsBucket, documentS3Key)) { + + try (OutputStream fileOs = new FileOutputStream(file)) { + IoUtils.copy(is, fileOs); + } + + String text = this.tesseract.doOcr(file); + + String ocrS3Key = ocrService.getS3Key(siteId, documentId, jobId); + this.s3Service.putObject(this.ocrDocumentsBucket, ocrS3Key, + text.getBytes(StandardCharsets.UTF_8), "text/plain"); + + ocrService.updateOcrScanStatus(this.awsServices, siteId, documentId, + OcrScanStatus.SUCCESSFUL); + + logger.log(String.format("setting OCR Scan Status: %s", OcrScanStatus.SUCCESSFUL)); + + } finally { + + if (!file.delete()) { + file.deleteOnExit(); + } + } + + } catch (IOException | TesseractException | RuntimeException e) { + e.printStackTrace(); + ocrService.updateOcrScanStatus(siteId, documentId, OcrScanStatus.FAILED); + logger.log(String.format("setting OCR Scan Status: %s", OcrScanStatus.FAILED)); + + ActionsService actionsService = this.awsServices.getExtension(ActionsService.class); + actionsService.updateActionStatus(siteId, documentId, ActionType.OCR, ActionStatus.FAILED); + } + } +} diff --git a/lambda-ocr-tesseract/src/main/java/com/formkiq/module/lambda/ocr/tesseract/TesseractWrapper.java b/lambda-ocr-tesseract/src/main/java/com/formkiq/module/lambda/ocr/tesseract/TesseractWrapper.java new file mode 100644 index 000000000..37268ac67 --- /dev/null +++ b/lambda-ocr-tesseract/src/main/java/com/formkiq/module/lambda/ocr/tesseract/TesseractWrapper.java @@ -0,0 +1,44 @@ +/** + * MIT License + * + * Copyright (c) 2018 - 2020 FormKiQ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.formkiq.module.lambda.ocr.tesseract; + +import java.io.File; +import net.sourceforge.tess4j.TesseractException; + +/** + * + * Wrapper for the Tesseract library. + * + */ +public interface TesseractWrapper { + + /** + * Performs OCR operation. + * + * @param imageFile an image file + * @return the recognized text + * @throws TesseractException TesseractException + */ + String doOcr(File imageFile) throws TesseractException; +} diff --git a/lambda-ocr-tesseract/src/main/java/com/formkiq/module/lambda/ocr/tesseract/TesseractWrapperImpl.java b/lambda-ocr-tesseract/src/main/java/com/formkiq/module/lambda/ocr/tesseract/TesseractWrapperImpl.java new file mode 100644 index 000000000..595cee608 --- /dev/null +++ b/lambda-ocr-tesseract/src/main/java/com/formkiq/module/lambda/ocr/tesseract/TesseractWrapperImpl.java @@ -0,0 +1,50 @@ +/** + * MIT License + * + * Copyright (c) 2018 - 2020 FormKiQ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.formkiq.module.lambda.ocr.tesseract; + +import java.io.File; +import net.sourceforge.tess4j.Tesseract; +import net.sourceforge.tess4j.TesseractException; + +/** + * + * {@link Tesseract} implement of {@link TesseractWrapper}. + * + */ +public class TesseractWrapperImpl implements TesseractWrapper { + + @Override + public String doOcr(final File imageFile) throws TesseractException { + + Tesseract tesseract = new Tesseract(); + tesseract.setDatapath("/opt/tesseract/share/tessdata/"); + tesseract.setLanguage("eng"); + tesseract.setPageSegMode(1); + tesseract.setOcrEngineMode(1); + + String text = tesseract.doOCR(imageFile); + return text; + } + +} diff --git a/lambda-ocr-tesseract/src/main/resources/cloudformation/template.yaml b/lambda-ocr-tesseract/src/main/resources/cloudformation/template.yaml new file mode 100644 index 000000000..3d54e6c25 --- /dev/null +++ b/lambda-ocr-tesseract/src/main/resources/cloudformation/template.yaml @@ -0,0 +1,200 @@ +#@ load("@ytt:data", "data") +#@ load("@ytt:assert", "assert") +--- +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: FormKiQ - Tesseract + +Parameters: + + AppEnvironment: + Type: String + Description: The Application Environment + AllowedPattern: ".+" + + LambdaMemory: + Type: Number + Description: The amount of memory used by lambda function (MB) + Default: 1024 + MinValue: 128 + MaxValue: 3008 + + LambdaTimeout: + Type: String + Description: The maximum amount of seconds lambda function will run for (seconds) + Default: 600 + + FormKiQType: + Description: The type of FormKiQ installation + Default: "core" + Type: String + AllowedValues: ["core", "enterprise"] + +Resources: + + TesseractProcessorLogGroup: + Type: AWS::Logs::LogGroup + Properties: + LogGroupName: + Fn::Sub: "/aws/vendedlogs/${AWS::StackName}/${TesseractProcessor}" + RetentionInDays: 90 + + TesseractLayer: + Type: AWS::Serverless::LayerVersion + Properties: + Description: Lambda Layer containing Tesseract binary + LayerName: + Fn::Sub: "${AWS::StackName}-${AppEnvironment}-tesseract-layer" + ContentUri: ./layer-tesseract-5.3.1.zip + + TesseractProcessor: + Type: AWS::Serverless::Function + DependsOn: + - TesseractLambdaRolePolicy + Properties: + Handler: com.formkiq.module.lambda.ocr.tesseract.OcrTesseractProcessor + Description: Lambda function to convert document to Tesseract + Runtime: java11 + Timeout: + Fn::Sub: "${LambdaTimeout}" + MemorySize: + Fn::Sub: "${LambdaMemory}" + Layers: + - Ref: TesseractLayer + CodeUri: ./formkiq-module-lambda-ocr-tesseract.zip + Tracing: Active + AutoPublishCodeSha256: #@ data.values.hash or assert.fail("missing version") + Environment: + Variables: + APP_ENVIRONMENT: + Fn::Sub: "${AppEnvironment}" + DEBUG: false + ENABLE_AWS_X_RAY: true + FormKiQType: + Ref: FormKiQType + DOCUMENTS_TABLE: + Fn::Sub: "{{resolve:ssm:/formkiq/${AppEnvironment}/dynamodb/DocumentsTableName}}" + DOCUMENTS_S3_BUCKET: + Fn::Sub: "{{resolve:ssm:/formkiq/${AppEnvironment}/s3/DocumentsS3Bucket}}" + OCR_S3_BUCKET: + Fn::Sub: "{{resolve:ssm:/formkiq/${AppEnvironment}/s3/OcrBucket}}" + SNS_DOCUMENT_EVENT: + Fn::Sub: "{{resolve:ssm:/formkiq/${AppEnvironment}/sns/DocumentEventArn}}" + Role: + Fn::GetAtt: + - TesseractLambdaRole + - Arn + Tags: + AppEnvironment: + Fn::Sub: "${AppEnvironment}" + Application: + Fn::Sub: "FormKiQ ${FormKiQType}" + StackName: + Fn::Sub: "${AWS::StackName}" + Events: + OcrSQSEvent: + Type: SQS + Properties: + Queue: + Fn::Sub: "{{resolve:ssm:/formkiq/${AppEnvironment}/sqs/OcrQueueArn}}" + BatchSize: 1 + + TesseractProcessorParameter: + Type: AWS::SSM::Parameter + Properties: + Description: "Lambda for processing records for Tesseract" + Name: + Fn::Sub: "/formkiq/${AppEnvironment}/lambda/TesseractProcessor" + Type: String + Value: + Ref: TesseractProcessor + Tags: + Application: + Fn::Sub: "FormKiQ ${FormKiQType}" + AppEnvironment: + Fn::Sub: "${AppEnvironment}" + StackName: + Fn::Sub: "${AWS::StackName}" + + TesseractLambdaRolePolicy: + Type: "AWS::IAM::Policy" + Properties: + PolicyName: Tesseract-lambdarole + Roles: + - + Ref: "TesseractLambdaRole" + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - logs:CreateLogGroup + - logs:CreateLogStream + - logs:PutLogEvents + Resource: "*" + - Effect: Allow + Action: + - s3:GetObject + Resource: + Fn::Join: + - '' + - - 'arn:aws:s3:::' + - Fn::Sub: "{{resolve:ssm:/formkiq/${AppEnvironment}/s3/DocumentsS3Bucket}}" + - "/*" + - Effect: Allow + Action: + - s3:GetObject + - s3:PutObject + Resource: + Fn::Join: + - '' + - - 'arn:aws:s3:::' + - Fn::Sub: "{{resolve:ssm:/formkiq/${AppEnvironment}/s3/OcrBucket}}" + - "/*" + - Effect: Allow + Action: + - sqs:ReceiveMessage + - sqs:DeleteMessage + - sqs:GetQueueAttributes + Resource: + Fn::Sub: "{{resolve:ssm:/formkiq/${AppEnvironment}/sqs/OcrQueueArn}}" + - Effect: Allow + Action: + - dynamodb:GetItem + - dynamodb:PutItem + - dynamodb:Query + - dynamodb:UpdateItem + Resource: + - Fn::Sub: "arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/{{resolve:ssm:/formkiq/${AppEnvironment}/dynamodb/DocumentsTableName}}" + - Effect: Allow + Action: + - sns:Publish + Resource: + - Fn::Sub: "{{resolve:ssm:/formkiq/${AppEnvironment}/sns/DocumentEventArn}}" + + + TesseractLambdaRole: + Type: AWS::IAM::Role + Properties: + Tags: + - Key: "Application" + Value: + Fn::Sub: "FormKiQ ${FormKiQType}" + - Key: "AppEnvironment" + Value: + Fn::Sub: "${AppEnvironment}" + - Key: "StackName" + Value: + Fn::Sub: "${AWS::StackName}" + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Principal: + Service: + - lambda.amazonaws.com + Action: + - sts:AssumeRole + Path: / + ManagedPolicyArns: + - arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess \ No newline at end of file diff --git a/lambda-ocr-tesseract/src/test/java/com/formkiq/module/lambda/ocr/tesseract/OcrTesseractProcessorTest.java b/lambda-ocr-tesseract/src/test/java/com/formkiq/module/lambda/ocr/tesseract/OcrTesseractProcessorTest.java new file mode 100644 index 000000000..e53e19bec --- /dev/null +++ b/lambda-ocr-tesseract/src/test/java/com/formkiq/module/lambda/ocr/tesseract/OcrTesseractProcessorTest.java @@ -0,0 +1,235 @@ +/** + * MIT License + * + * Copyright (c) 2018 - 2020 FormKiQ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.formkiq.module.lambda.ocr.tesseract; + +import static com.formkiq.aws.dynamodb.SiteIdKeyGenerator.createS3Key; +import static com.formkiq.testutils.aws.DynamoDbExtension.DOCUMENTS_TABLE; +import static com.formkiq.testutils.aws.TestServices.BUCKET_NAME; +import static com.formkiq.testutils.aws.TestServices.OCR_BUCKET_NAME; +import static org.junit.jupiter.api.Assertions.assertEquals; +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import com.amazonaws.services.lambda.runtime.Context; +import com.formkiq.aws.dynamodb.DynamicObject; +import com.formkiq.aws.dynamodb.DynamoDbConnectionBuilder; +import com.formkiq.aws.dynamodb.objects.MimeType; +import com.formkiq.aws.s3.S3ConnectionBuilder; +import com.formkiq.aws.s3.S3Service; +import com.formkiq.aws.sns.SnsConnectionBuilder; +import com.formkiq.aws.sqs.SqsMessageRecord; +import com.formkiq.aws.sqs.SqsMessageRecords; +import com.formkiq.module.actions.Action; +import com.formkiq.module.actions.ActionStatus; +import com.formkiq.module.actions.ActionType; +import com.formkiq.module.actions.services.ActionsService; +import com.formkiq.module.lambdaservices.AwsServiceCache; +import com.formkiq.module.ocr.DocumentOcrService; +import com.formkiq.module.ocr.Ocr; +import com.formkiq.module.ocr.OcrEngine; +import com.formkiq.module.ocr.OcrScanStatus; +import com.formkiq.testutils.aws.DynamoDbExtension; +import com.formkiq.testutils.aws.DynamoDbTestServices; +import com.formkiq.testutils.aws.LambdaContextRecorder; +import com.formkiq.testutils.aws.LocalStackExtension; +import com.formkiq.testutils.aws.TestServices; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +/** + * + * Unit Tests {@link OcrTesseractProcessor}. + * + */ +@ExtendWith(DynamoDbExtension.class) +@ExtendWith(LocalStackExtension.class) +class OcrTesseractProcessorTest { + + /** {@link ActionsService}. */ + private static ActionsService actionsService; + /** {@link Gson}. */ + private static final Gson GSON = new GsonBuilder().create(); + /** Ocr Text. */ + private static final String OCR_TEXT = "this is test data"; + /** {@link DocumentOcrService}. */ + private static DocumentOcrService ocrService; + /** {@link OcrTesseractProcessor}. */ + private static OcrTesseractProcessor processor; + /** {@link S3Service}. */ + private static S3Service s3; + + @BeforeAll + public static void beforeAll() throws Exception { + + DynamoDbConnectionBuilder dbConnection = DynamoDbTestServices.getDynamoDbConnection(); + S3ConnectionBuilder s3Connection = TestServices.getS3Connection(null); + SnsConnectionBuilder sns = TestServices.getSnsConnection(null); + + TesseractWrapperData wrapper = new TesseractWrapperData(OCR_TEXT); + processor = new OcrTesseractProcessor(Map.of("DOCUMENTS_TABLE", DOCUMENTS_TABLE, + "DOCUMENTS_S3_BUCKET", BUCKET_NAME, "OCR_S3_BUCKET", OCR_BUCKET_NAME), dbConnection, + s3Connection, sns, wrapper); + + AwsServiceCache awsServices = processor.getAwsServices(); + ocrService = awsServices.getExtension(DocumentOcrService.class); + s3 = awsServices.getExtension(S3Service.class); + actionsService = awsServices.getExtension(ActionsService.class); + } + + /** {@link Context}. */ + private Context context = new LambdaContextRecorder(); + + /** + * Test S3 File doesn't exist. + * + * @throws Exception Exception + */ + @Test + void testHandleRequest01() throws Exception { + // given + for (String siteId : Arrays.asList("default", UUID.randomUUID().toString())) { + + String documentId = UUID.randomUUID().toString(); + String jobId = UUID.randomUUID().toString(); + + List actions = Arrays.asList(new Action().type(ActionType.OCR)); + actionsService.saveActions(siteId, documentId, actions); + + Ocr ocr = new Ocr().siteId(siteId).documentId(documentId).jobId(jobId) + .engine(OcrEngine.TESSERACT).status(OcrScanStatus.REQUESTED); + ocrService.save(ocr); + + SqsMessageRecord record = new SqsMessageRecord().body(GSON.toJson(Map.of("siteId", siteId, + "documentId", documentId, "jobId", jobId, "contentType", MimeType.MIME_JPEG))); + SqsMessageRecords records = new SqsMessageRecords().records(Arrays.asList(record)); + + String json = GSON.toJson(records); + InputStream is = new ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8)); + + // when + processor.handleRequest(is, null, this.context); + + // then + DynamicObject obj = ocrService.get(siteId, documentId); + assertEquals("failed", obj.get("ocrStatus")); + + actions = actionsService.getActions(siteId, documentId); + assertEquals(ActionStatus.FAILED, actions.get(0).status()); + } + } + + /** + * Test Successful OCR. + * + * @throws Exception Exception + */ + @Test + void testHandleRequest02() throws Exception { + // given + for (String siteId : Arrays.asList("default", UUID.randomUUID().toString())) { + + String documentId = UUID.randomUUID().toString(); + String jobId = UUID.randomUUID().toString(); + + List actions = Arrays.asList(new Action().type(ActionType.OCR)); + actionsService.saveActions(siteId, documentId, actions); + + String documentS3Key = createS3Key(siteId, documentId); + s3.putObject(BUCKET_NAME, documentS3Key, "testdata".getBytes(StandardCharsets.UTF_8), + "text/plain"); + + Ocr ocr = new Ocr().siteId(siteId).documentId(documentId).jobId(jobId) + .engine(OcrEngine.TESSERACT).status(OcrScanStatus.REQUESTED); + ocrService.save(ocr); + + SqsMessageRecord record = + new SqsMessageRecord().body(GSON.toJson(Map.of("siteId", siteId, "documentId", documentId, + "jobId", jobId, "contentType", MimeType.MIME_JPEG.getContentType()))); + SqsMessageRecords records = new SqsMessageRecords().records(Arrays.asList(record)); + + String json = GSON.toJson(records); + InputStream is = new ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8)); + + // when + processor.handleRequest(is, null, this.context); + + // then + DynamicObject obj = ocrService.get(siteId, documentId); + assertEquals("successful", obj.get("ocrStatus")); + + String ocrS3Key = ocrService.getS3Key(siteId, documentId, jobId); + assertEquals(OCR_TEXT, s3.getContentAsString(OCR_BUCKET_NAME, ocrS3Key, null)); + + actions = actionsService.getActions(siteId, documentId); + assertEquals(ActionStatus.COMPLETE, actions.get(0).status()); + } + } + + /** + * Test Content Type unknown. + * + * @throws Exception Exception + */ + @Test + void testHandleRequest03() throws Exception { + // given + for (String siteId : Arrays.asList("default", UUID.randomUUID().toString())) { + + String documentId = UUID.randomUUID().toString(); + String jobId = UUID.randomUUID().toString(); + + List actions = Arrays.asList(new Action().type(ActionType.OCR)); + actionsService.saveActions(siteId, documentId, actions); + + Ocr ocr = new Ocr().siteId(siteId).documentId(documentId).jobId(jobId) + .engine(OcrEngine.TESSERACT).status(OcrScanStatus.REQUESTED); + ocrService.save(ocr); + + SqsMessageRecord record = new SqsMessageRecord().body(GSON.toJson(Map.of("siteId", siteId, + "documentId", documentId, "jobId", jobId, "contentType", MimeType.MIME_DOCX))); + SqsMessageRecords records = new SqsMessageRecords().records(Arrays.asList(record)); + + String json = GSON.toJson(records); + InputStream is = new ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8)); + + // when + processor.handleRequest(is, null, this.context); + + // then + DynamicObject obj = ocrService.get(siteId, documentId); + assertEquals("failed", obj.get("ocrStatus")); + + actions = actionsService.getActions(siteId, documentId); + assertEquals(ActionStatus.FAILED, actions.get(0).status()); + } + } + +} diff --git a/lambda-ocr-tesseract/src/test/java/com/formkiq/module/lambda/ocr/tesseract/TesseractWrapperData.java b/lambda-ocr-tesseract/src/test/java/com/formkiq/module/lambda/ocr/tesseract/TesseractWrapperData.java new file mode 100644 index 000000000..7326238da --- /dev/null +++ b/lambda-ocr-tesseract/src/test/java/com/formkiq/module/lambda/ocr/tesseract/TesseractWrapperData.java @@ -0,0 +1,53 @@ +/** + * MIT License + * + * Copyright (c) 2018 - 2020 FormKiQ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.formkiq.module.lambda.ocr.tesseract; + +import java.io.File; +import net.sourceforge.tess4j.TesseractException; + +/** + * + * Return Test data for {@link TesseractWrapper}. + * + */ +public class TesseractWrapperData implements TesseractWrapper { + + /** {@link String}. */ + private String data; + + /** + * constructor. + * + * @param s {@link String} + */ + public TesseractWrapperData(final String s) { + this.data = s; + } + + @Override + public String doOcr(final File imageFile) throws TesseractException { + return this.data; + } + +} diff --git a/lambda-s3/.classpath b/lambda-s3/.classpath index 66f8050c0..58920f4a9 100644 --- a/lambda-s3/.classpath +++ b/lambda-s3/.classpath @@ -3,29 +3,41 @@ - + - + - + - + + + + + + + + + + + + + diff --git a/lambda-s3/build.gradle b/lambda-s3/build.gradle index b889c2795..da483d86e 100644 --- a/lambda-s3/build.gradle +++ b/lambda-s3/build.gradle @@ -1,8 +1,32 @@ description = "FormKiQ Core - Lambda S3 to Dynamodb" +sourceSets { + integration { + java.srcDir "$projectDir/src/integration/java" + resources.srcDir "$projectDir/src/integration/resources" + compileClasspath += main.output + test.output + runtimeClasspath += main.output + test.output + } +} + +configurations { + integrationImplementation.extendsFrom testImplementation + integrationRuntime.extendsFrom testRuntime +} + +def getCmd() { + String os = System.getProperty("os.name").toLowerCase() + return os.contains("win") ? "cmd" : "bash" +} + +def getCmdParam() { + String os = System.getProperty("os.name").toLowerCase() + return os.contains("win") ? "/c" : "-c" +} + dependencies { - annotationProcessor group: 'com.formkiq', name: 'graalvm-annotations-processor', version: '1.2.0' + annotationProcessor group: 'com.formkiq', name: 'graalvm-annotations-processor', version: '1.3.0' implementation project(':aws-s3') implementation project(':aws-sns') @@ -17,29 +41,27 @@ dependencies { implementation project(':http') implementation group: 'com.amazonaws', name: 'aws-lambda-java-core', version: '1.2.2' - implementation group: 'com.google.code.gson', name: 'gson', version: '2.10' + implementation group: 'com.google.code.gson', name: 'gson', version: '2.10.1' implementation group: 'com.formkiq', name: 'lambda-runtime-graalvm', version:'2.3.1' - implementation group: 'com.formkiq', name: 'graalvm-annotations', version: '1.1.0' - implementation group: 'com.formkiq.stacks', name: 'client', version:'1.10.0-SNAPSHOT' + implementation group: 'com.formkiq', name: 'graalvm-annotations', version: '1.2.0' + implementation group: 'com.formkiq.stacks', name: 'client', version:'1.10.0' - implementation group: 'org.slf4j', name: 'slf4j-simple', version: '2.0.6' + implementation group: 'org.slf4j', name: 'slf4j-simple', version: '2.0.7' testImplementation project(':aws-sqs') testImplementation project(':fkq-test-utils') - testImplementation group: 'junit', name: 'junit', version:'4.+' testImplementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.12.0' - testImplementation group: 'software.amazon.awssdk', name: 'dynamodb', version: '2.19.2' - testImplementation group: 'software.amazon.awssdk', name: 's3', version: '2.19.2' + testImplementation group: 'software.amazon.awssdk', name: 'dynamodb', version: '2.20.33' + testImplementation group: 'software.amazon.awssdk', name: 's3', version: '2.20.33' testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version:'5.9.1' - testImplementation group: 'org.junit.vintage', name: 'junit-vintage-engine', version:'5.9.1' testImplementation group: 'org.testcontainers', name: 'testcontainers', version: '1.17.6' testImplementation group: 'org.testcontainers', name: 'junit-jupiter', version: '1.17.6' testImplementation group: 'org.testcontainers', name: 'localstack', version: '1.17.6' - testImplementation group: 'com.amazonaws', name: 'aws-java-sdk', version: '1.12.370' - testImplementation group: 'org.mock-server', name: 'mockserver-netty', version: '5.14.0' + testImplementation group: 'com.amazonaws', name: 'aws-java-sdk', version: '1.12.436' + testImplementation group: 'org.mock-server', name: 'mockserver-netty', version: '5.15.0' } compileJava { @@ -59,14 +81,13 @@ nativeImage { test { failFast = true - exclude 'com/formkiq/stacks/lambda/s3/awstest/**' useJUnitPlatform() } -task testaws(type: Test) { - description = 'Runs AWS integration tests.' - outputs.upToDateWhen {false} - include 'com/formkiq/stacks/lambda/s3/awstest/**' +task integrationTest(type: Test) { + testClassesDirs = sourceSets.integration.output.classesDirs + classpath = sourceSets.integration.runtimeClasspath + useJUnitPlatform() } task buildZip(type: Zip) { @@ -100,7 +121,7 @@ task assembleTemplate { } exec { - commandLine "bash", "-c", "ytt --data-value hash=${sha256} -f src/main/resources/cloudformation/template-sar.yaml > ${buildDir}/distributions/formkiq-core/sam/storage/template.yaml" + commandLine getCmd(), getCmdParam(), "ytt --data-value hash=${sha256} -f src/main/resources/cloudformation/template-sar.yaml > ${buildDir}/distributions/formkiq-core/sam/storage/template.yaml" } } } diff --git a/lambda-s3/src/test/java/com/formkiq/stacks/lambda/s3/awstest/AbstractAwsTest.java b/lambda-s3/src/integration/java/com/formkiq/stacks/lambda/s3/awstest/AbstractAwsTest.java similarity index 92% rename from lambda-s3/src/test/java/com/formkiq/stacks/lambda/s3/awstest/AbstractAwsTest.java rename to lambda-s3/src/integration/java/com/formkiq/stacks/lambda/s3/awstest/AbstractAwsTest.java index 2e4191fcd..d022c95ec 100644 --- a/lambda-s3/src/test/java/com/formkiq/stacks/lambda/s3/awstest/AbstractAwsTest.java +++ b/lambda-s3/src/integration/java/com/formkiq/stacks/lambda/s3/awstest/AbstractAwsTest.java @@ -23,9 +23,9 @@ */ package com.formkiq.stacks.lambda.s3.awstest; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.IOException; -import org.junit.BeforeClass; +import org.junit.jupiter.api.BeforeAll; import com.formkiq.aws.dynamodb.DynamoDbConnectionBuilder; import com.formkiq.aws.s3.S3ConnectionBuilder; import com.formkiq.aws.s3.S3ObjectMetadata; @@ -94,21 +94,25 @@ public abstract class AbstractAwsTest { * * @throws IOException IOException */ - @BeforeClass + @BeforeAll public static void beforeClass() throws IOException { awsregion = Region.of(System.getProperty("testregion")); String awsprofile = System.getProperty("testprofile"); appenvironment = System.getProperty("testappenvironment"); - sqsBuilder = new SqsConnectionBuilder().setCredentials(awsprofile).setRegion(awsregion); + boolean enableAwsXray = false; + sqsBuilder = + new SqsConnectionBuilder(enableAwsXray).setCredentials(awsprofile).setRegion(awsregion); - ssmBuilder = new SsmConnectionBuilder().setCredentials(awsprofile).setRegion(awsregion); + ssmBuilder = + new SsmConnectionBuilder(enableAwsXray).setCredentials(awsprofile).setRegion(awsregion); final S3ConnectionBuilder s3Builder = - new S3ConnectionBuilder().setCredentials(awsprofile).setRegion(awsregion); + new S3ConnectionBuilder(enableAwsXray).setCredentials(awsprofile).setRegion(awsregion); - snsBuilder = new SnsConnectionBuilder().setCredentials(awsprofile).setRegion(awsregion); + snsBuilder = + new SnsConnectionBuilder(enableAwsXray).setCredentials(awsprofile).setRegion(awsregion); sqsService = new SqsService(sqsBuilder); s3Service = new S3Service(s3Builder); @@ -128,7 +132,8 @@ public static void beforeClass() throws IOException { String documentsTable = ssmService.getParameterValue("/formkiq/" + appenvironment + "/dynamodb/DocumentsTableName"); - dbConnection = new DynamoDbConnectionBuilder().setCredentials(awsprofile).setRegion(awsregion); + dbConnection = + new DynamoDbConnectionBuilder(false).setCredentials(awsprofile).setRegion(awsregion); documentService = new DocumentServiceImpl(dbConnection, documentsTable, new DocumentVersionServiceNoVersioning()); searchService = diff --git a/lambda-s3/src/test/java/com/formkiq/stacks/lambda/s3/awstest/AwsResourceTest.java b/lambda-s3/src/integration/java/com/formkiq/stacks/lambda/s3/awstest/AwsResourceTest.java similarity index 96% rename from lambda-s3/src/test/java/com/formkiq/stacks/lambda/s3/awstest/AwsResourceTest.java rename to lambda-s3/src/integration/java/com/formkiq/stacks/lambda/s3/awstest/AwsResourceTest.java index 72545640a..f4563ee35 100644 --- a/lambda-s3/src/test/java/com/formkiq/stacks/lambda/s3/awstest/AwsResourceTest.java +++ b/lambda-s3/src/integration/java/com/formkiq/stacks/lambda/s3/awstest/AwsResourceTest.java @@ -25,11 +25,11 @@ import static com.formkiq.aws.dynamodb.SiteIdKeyGenerator.DEFAULT_SITE_ID; import static com.formkiq.stacks.dynamodb.DocumentService.MAX_RESULTS; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.net.URL; import java.net.http.HttpClient; import java.net.http.HttpRequest; @@ -48,8 +48,10 @@ import java.util.Map; import java.util.Optional; import java.util.UUID; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; import com.formkiq.aws.dynamodb.PaginationResults; import com.formkiq.aws.dynamodb.model.DocumentItem; import com.formkiq.aws.dynamodb.model.DocumentTag; @@ -186,7 +188,8 @@ private String subscribeToSns(final String topicArn, final String queueUrl) { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT * 2) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT * 2) public void testAddDeleteFile01() throws Exception { // given String key = UUID.randomUUID().toString(); @@ -234,7 +237,8 @@ public void testAddDeleteFile01() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testAddDeleteFile02() throws Exception { // given final Long contentLength = Long.valueOf(36); @@ -270,7 +274,8 @@ public void testAddDeleteFile02() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testAddDeleteFile03() throws Exception { // given final int statusCode = 200; @@ -323,7 +328,8 @@ public void testAddDeleteFile03() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testAddDeleteFile04() throws Exception { // given String key = UUID.randomUUID().toString(); @@ -459,7 +465,8 @@ public void testStageDocumentNotifications() { * * @throws Exception Exception */ - @Test(timeout = TEST_TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TEST_TIMEOUT) public void testUpdateStagingFile01() throws Exception { // given final String siteId = DEFAULT_SITE_ID; diff --git a/lambda-s3/src/test/java/com/formkiq/stacks/lambda/s3/awstest/LambdaFunctionConfigurationComparator.java b/lambda-s3/src/integration/java/com/formkiq/stacks/lambda/s3/awstest/LambdaFunctionConfigurationComparator.java similarity index 100% rename from lambda-s3/src/test/java/com/formkiq/stacks/lambda/s3/awstest/LambdaFunctionConfigurationComparator.java rename to lambda-s3/src/integration/java/com/formkiq/stacks/lambda/s3/awstest/LambdaFunctionConfigurationComparator.java diff --git a/lambda-s3/src/main/java/com/formkiq/stacks/lambda/s3/DocumentAction.java b/lambda-s3/src/main/java/com/formkiq/stacks/lambda/s3/DocumentAction.java new file mode 100644 index 000000000..f1ed93582 --- /dev/null +++ b/lambda-s3/src/main/java/com/formkiq/stacks/lambda/s3/DocumentAction.java @@ -0,0 +1,47 @@ +/** + * MIT License + * + * Copyright (c) 2018 - 2020 FormKiQ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.formkiq.stacks.lambda.s3; + +import java.io.IOException; +import com.amazonaws.services.lambda.runtime.LambdaLogger; +import com.formkiq.module.actions.Action; + +/** + * + * Document Action interface. + * + */ +public interface DocumentAction { + + /** + * Run Action. + * + * @param logger {@link LambdaLogger} + * @param siteId {@link String} + * @param documentId {@link String} + * @param action {@link Action} + * @throws IOException IOException + */ + void run(LambdaLogger logger, String siteId, String documentId, Action action) throws IOException; +} diff --git a/lambda-s3/src/main/java/com/formkiq/stacks/lambda/s3/DocumentActionsProcessor.java b/lambda-s3/src/main/java/com/formkiq/stacks/lambda/s3/DocumentActionsProcessor.java index c6ea05028..2cc29b0d3 100644 --- a/lambda-s3/src/main/java/com/formkiq/stacks/lambda/s3/DocumentActionsProcessor.java +++ b/lambda-s3/src/main/java/com/formkiq/stacks/lambda/s3/DocumentActionsProcessor.java @@ -36,7 +36,6 @@ import java.net.http.HttpClient.Redirect; import java.net.http.HttpRequest; import java.net.http.HttpResponse; -import java.net.http.HttpResponse.BodyHandlers; import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; @@ -52,6 +51,7 @@ import com.amazonaws.services.lambda.runtime.RequestHandler; import com.formkiq.aws.dynamodb.DbKeys; import com.formkiq.aws.dynamodb.DynamoDbConnectionBuilder; +import com.formkiq.aws.dynamodb.DynamoDbConnectionBuilderExtension; import com.formkiq.aws.dynamodb.DynamoDbService; import com.formkiq.aws.dynamodb.DynamoDbServiceImpl; import com.formkiq.aws.dynamodb.SiteIdKeyGenerator; @@ -60,16 +60,18 @@ import com.formkiq.aws.dynamodb.model.DocumentTag; import com.formkiq.aws.dynamodb.model.DocumentToFulltextDocument; import com.formkiq.aws.dynamodb.model.DynamicDocumentItem; +import com.formkiq.aws.dynamodb.objects.MimeType; import com.formkiq.aws.s3.PresignGetUrlConfig; import com.formkiq.aws.s3.S3ConnectionBuilder; import com.formkiq.aws.s3.S3Service; +import com.formkiq.aws.s3.S3ServiceExtension; import com.formkiq.aws.sns.SnsConnectionBuilder; import com.formkiq.aws.ssm.SsmConnectionBuilder; import com.formkiq.aws.ssm.SsmService; import com.formkiq.aws.ssm.SsmServiceCache; +import com.formkiq.aws.ssm.SsmServiceExtension; import com.formkiq.graalvm.annotations.Reflectable; import com.formkiq.graalvm.annotations.ReflectableClass; -import com.formkiq.graalvm.annotations.ReflectableClasses; import com.formkiq.graalvm.annotations.ReflectableField; import com.formkiq.graalvm.annotations.ReflectableImport; import com.formkiq.module.actions.Action; @@ -82,6 +84,7 @@ import com.formkiq.module.actions.services.NextActionPredicate; import com.formkiq.module.documentevents.DocumentEvent; import com.formkiq.module.lambdaservices.AwsServiceCache; +import com.formkiq.module.lambdaservices.ClassServiceExtension; import com.formkiq.module.typesense.TypeSenseService; import com.formkiq.module.typesense.TypeSenseServiceImpl; import com.formkiq.stacks.client.FormKiqClientConnection; @@ -89,14 +92,14 @@ import com.formkiq.stacks.client.models.UpdateFulltext; import com.formkiq.stacks.client.models.UpdateFulltextTag; import com.formkiq.stacks.client.requests.AddDocumentOcrRequest; -import com.formkiq.stacks.client.requests.GetDocumentOcrRequest; import com.formkiq.stacks.client.requests.OcrParseType; import com.formkiq.stacks.client.requests.SetDocumentAntivirusRequest; import com.formkiq.stacks.client.requests.UpdateDocumentFulltextRequest; -import com.formkiq.stacks.common.formats.MimeType; +import com.formkiq.stacks.dynamodb.ConfigService; +import com.formkiq.stacks.dynamodb.ConfigServiceExtension; import com.formkiq.stacks.dynamodb.DocumentItemToDynamicDocumentItem; import com.formkiq.stacks.dynamodb.DocumentService; -import com.formkiq.stacks.dynamodb.DocumentServiceImpl; +import com.formkiq.stacks.dynamodb.DocumentServiceExtension; import com.formkiq.stacks.dynamodb.DocumentVersionService; import com.formkiq.stacks.dynamodb.DocumentVersionServiceDynamoDb; import com.formkiq.stacks.dynamodb.DocumentVersionServiceExtension; @@ -112,12 +115,11 @@ @Reflectable @ReflectableImport(classes = {DocumentEvent.class, DocumentVersionServiceDynamoDb.class, DocumentVersionServiceNoVersioning.class}) -@ReflectableClasses({ - @ReflectableClass(className = UpdateFulltextTag.class, allPublicConstructors = true, - fields = {@ReflectableField(name = "key"), @ReflectableField(name = "value"), - @ReflectableField(name = "values")}), - @ReflectableClass(className = UpdateFulltext.class, allPublicConstructors = true, - fields = {@ReflectableField(name = "content"), @ReflectableField(name = "contentUrls")})}) +@ReflectableClass(className = UpdateFulltextTag.class, allPublicConstructors = true, + fields = {@ReflectableField(name = "key"), @ReflectableField(name = "value"), + @ReflectableField(name = "values")}) +@ReflectableClass(className = UpdateFulltext.class, allPublicConstructors = true, + fields = {@ReflectableField(name = "content"), @ReflectableField(name = "contentUrls")}) public class DocumentActionsProcessor implements RequestHandler, Void>, DbKeys { /** Default Maximum for Typesense Content. */ @@ -126,8 +128,6 @@ public class DocumentActionsProcessor implements RequestHandler map, final Region a final S3ConnectionBuilder s3, final SsmConnectionBuilder ssm, final SnsConnectionBuilder sns) { - this.serviceCache = new AwsServiceCache().environment(map); - DocumentVersionServiceExtension dsExtension = new DocumentVersionServiceExtension(); - DocumentVersionService versionService = dsExtension.loadService(this.serviceCache); - - this.s3Service = new S3Service(s3); - this.documentService = - new DocumentServiceImpl(dbBuilder, map.get("DOCUMENTS_TABLE"), versionService); + AwsServiceCache.register(DynamoDbConnectionBuilder.class, + new DynamoDbConnectionBuilderExtension(dbBuilder)); + AwsServiceCache.register(SsmConnectionBuilder.class, + new ClassServiceExtension(ssm)); + AwsServiceCache.register(SsmService.class, new SsmServiceExtension()); + AwsServiceCache.register(S3Service.class, new S3ServiceExtension(s3)); + AwsServiceCache.register(DocumentVersionService.class, new DocumentVersionServiceExtension()); + AwsServiceCache.register(DocumentService.class, new DocumentServiceExtension()); + AwsServiceCache.register(ConfigService.class, new ConfigServiceExtension()); + AwsServiceCache.register(FormKiqClientV1.class, + new FormKiQClientV1Extension(awsRegion, awsCredentials)); + + this.serviceCache = + new AwsServiceCache().environment(map).debug("true".equals(map.get("DEBUG"))); + + this.s3Service = this.serviceCache.getExtension(S3Service.class); + this.documentService = this.serviceCache.getExtension(DocumentService.class); this.actionsService = new ActionsServiceDynamoDb(dbBuilder, map.get("DOCUMENTS_TABLE")); this.dbService = new DynamoDbServiceImpl(dbBuilder, map.get("DOCUMENTS_TABLE")); String snsDocumentEvent = map.get("SNS_DOCUMENT_EVENT"); @@ -199,14 +211,13 @@ protected DocumentActionsProcessor(final Map map, final Region a String typeSenseApiKey = ssmService.getParameterValue("/formkiq/" + appEnvironment + "/typesense/ApiKey"); - this.typesense = - new TypeSenseServiceImpl(typeSenseHost, typeSenseApiKey, awsRegion, awsCredentials); + if (typeSenseHost != null && typeSenseApiKey != null) { + this.typesense = + new TypeSenseServiceImpl(typeSenseHost, typeSenseApiKey, awsRegion, awsCredentials); + } this.documentsIamUrl = ssmService.getParameterValue("/formkiq/" + appEnvironment + "/api/DocumentsIamUrl"); - this.documentsBucket = - ssmService.getParameterValue("/formkiq/" + appEnvironment + "/s3/DocumentsS3Bucket"); - this.ocrBucket = ssmService.getParameterValue("/formkiq/" + appEnvironment + "/s3/OcrBucket"); this.fkqConnection = new FormKiqClientConnection(this.documentsIamUrl).region(awsRegion); @@ -266,51 +277,16 @@ private String buildWebhookBody(final String siteId, final String documentId, return this.gson.toJson(Map.of("documents", documents)); } - /** - * Find Content Url. - * - * @param siteId {@link String} - * @param item {@link DocumentItem} - * @return {@link List} {@link String} - * @throws InterruptedException InterruptedException - * @throws IOException IOException - */ - @SuppressWarnings("unchecked") - private List findContentUrls(final String siteId, final DocumentItem item) - throws IOException, InterruptedException { - - List urls = null; - String documentId = item.getDocumentId(); - String s3Key = SiteIdKeyGenerator.createS3Key(siteId, documentId); - - if (MimeType.isPlainText(item.getContentType())) { - - PresignGetUrlConfig config = new PresignGetUrlConfig() - .contentDispositionByPath(item.getPath(), false).contentType(item.getContentType()); - - String bucket = - MimeType.isPlainText(item.getContentType()) ? this.documentsBucket : this.ocrBucket; - - String url = - this.s3Service.presignGetUrl(bucket, s3Key, Duration.ofHours(1), null, config).toString(); - urls = Arrays.asList(url); - - } else { + private void debug(final LambdaLogger logger, final String siteId, final DocumentItem item) { + if (isDebug()) { + String s = String.format( + "{\"siteId\": \"%s\",\"documentId\": \"%s\",\"path\": \"%s\",\"userId\": \"%s\"," + + "\"s3Version\": \"%s\",\"contentType\": \"%s\"}", + siteId, item.getDocumentId(), item.getPath(), item.getUserId(), item.getS3version(), + item.getContentType()); - GetDocumentOcrRequest req = new GetDocumentOcrRequest().siteId(siteId).documentId(documentId); - - req.addQueryParameter("contentUrl", "true"); - req.addQueryParameter("text", "true"); - - HttpResponse response = this.formkiqClient.getDocumentOcrAsHttpResponse(req); - Map map = this.gson.fromJson(response.body(), Map.class); - - if (map != null && map.containsKey("contentUrls")) { - urls = (List) map.get("contentUrls"); - } + logger.log(s); } - - return urls; } private int getCharacterMax(final Action action) { @@ -321,6 +297,7 @@ private int getCharacterMax(final Action action) { /** * Get Content from {@link Action}. * + * @param dcFunc {@link DocumentContentFunction} * @param action {@link Action} * @param contentUrls {@link List} {@link String} * @return {@link String} @@ -328,10 +305,10 @@ private int getCharacterMax(final Action action) { * @throws IOException IOException * @throws InterruptedException InterruptedException */ - private String getContent(final Action action, final List contentUrls) - throws URISyntaxException, IOException, InterruptedException { + private String getContent(final DocumentContentFunction dcFunc, final Action action, + final List contentUrls) throws URISyntaxException, IOException, InterruptedException { - StringBuilder sb = getContentUrls(contentUrls); + StringBuilder sb = dcFunc.getContentUrls(contentUrls); int characterMax = getCharacterMax(action); @@ -342,31 +319,6 @@ private String getContent(final Action action, final List contentUrls) return content; } - /** - * Get Content from external urls. - * - * @param contentUrls {@link List} {@link String} - * @return {@link StringBuilder} - * @throws URISyntaxException URISyntaxException - * @throws IOException IOException - * @throws InterruptedException InterruptedException - */ - private StringBuilder getContentUrls(final List contentUrls) - throws URISyntaxException, IOException, InterruptedException { - - StringBuilder sb = new StringBuilder(); - - for (String contentUrl : contentUrls) { - HttpRequest req = - HttpRequest.newBuilder(new URI(contentUrl)).timeout(Duration.ofMinutes(1)).build(); - HttpResponse response = - HttpClient.newBuilder().build().send(req, BodyHandlers.ofString()); - sb.append(response.body()); - } - - return sb; - } - /** * Get ParseTypes from {@link Action} parameters. * @@ -399,11 +351,13 @@ List getOcrParseTypes(final Action action) { * @return {@link URL} */ private URL getS3Url(final String siteId, final String documentId, final DocumentItem item) { + + String documentsBucket = this.serviceCache.environment("DOCUMENTS_S3_BUCKET"); Duration duration = Duration.ofDays(1); PresignGetUrlConfig config = new PresignGetUrlConfig().contentDispositionByPath(item.getPath(), false); String s3key = createS3Key(siteId, documentId); - return this.s3Service.presignGetUrl(this.documentsBucket, s3key, duration, null, config); + return this.s3Service.presignGetUrl(documentsBucket, s3key, duration, null, config); } @SuppressWarnings("unchecked") @@ -412,7 +366,7 @@ public Void handleRequest(final Map map, final Context context) LambdaLogger logger = context.getLogger(); - if ("true".equals(System.getenv("DEBUG"))) { + if (isDebug()) { String json = this.gson.toJson(map); logger.log(json); } @@ -425,10 +379,34 @@ public Void handleRequest(final Map map, final Context context) throw new RuntimeException(e); } - return null; } + private boolean isDebug() { + return this.serviceCache.debug(); + } + + /** + * Log Start of {@link Action}. + * + * @param logger {@link LambdaLogger} + * @param type {@link String} + * @param siteId {@link String} + * @param documentId {@link String} + * @param action {@link Action} + */ + private void logAction(final LambdaLogger logger, final String type, final String siteId, + final String documentId, final Action action) { + + String s = String.format( + "{\"type\",\"%s\",\"siteId\":\"%s\",\"documentId\":\"%s\",\"actionType\":\"%s\"," + + "\"actionStatus\":\"%s\",\"userId\":\"%s\",\"parameters\": \"%s\"}", + type, siteId, documentId, action.type(), action.status(), action.userId(), + action.parameters()); + + logger.log(s); + } + /** * Process Action. * @@ -442,9 +420,20 @@ public Void handleRequest(final Map map, final Context context) private void processAction(final LambdaLogger logger, final String siteId, final String documentId, final Action action) throws IOException, InterruptedException { - logger.log(String.format("processing action %s", action.type())); + logAction(logger, "action start", siteId, documentId, action); + + if (ActionType.DOCUMENTTAGGING.equals(action.type())) { - if (ActionType.OCR.equals(action.type())) { + DocumentTaggingAction dtAction = new DocumentTaggingAction(this.serviceCache); + dtAction.run(logger, siteId, documentId, action); + + List updatedActions = this.actionsService.updateActionStatus(siteId, documentId, + action.type(), ActionStatus.COMPLETE); + + action.status(ActionStatus.COMPLETE); + this.notificationService.publishNextActionEvent(updatedActions, siteId, documentId); + + } else if (ActionType.OCR.equals(action.type())) { List parseTypes = getOcrParseTypes(action); Map parameters = @@ -458,21 +447,34 @@ private void processAction(final LambdaLogger logger, final String siteId, } else if (ActionType.FULLTEXT.equals(action.type())) { + ActionStatus status = ActionStatus.COMPLETE; DocumentItem item = this.documentService.findDocument(siteId, documentId); - List contentUrls = findContentUrls(siteId, item); + debug(logger, siteId, item); + + logger.log("contenttype: " + item.getContentType()); + logger.log("MIMETYPE: " + MimeType.isPlainText(item.getContentType())); + logger.log("DOCUMENTS_S3_BUCKET: " + this.serviceCache.environment("DOCUMENTS_S3_BUCKET")); + DocumentContentFunction documentContentFunc = new DocumentContentFunction(this.serviceCache); + List contentUrls = documentContentFunc.getContentUrls(siteId, item); boolean moduleFulltext = this.serviceCache.hasModule("fulltext"); if (moduleFulltext) { updateOpensearchFulltext(logger, siteId, documentId, action, contentUrls); + } else if (this.typesense != null) { + updateTypesense(documentContentFunc, siteId, documentId, action, contentUrls); } else { - updateTypesense(siteId, documentId, action, contentUrls); + status = ActionStatus.FAILED; } - List updatedActions = this.actionsService.updateActionStatus(siteId, documentId, - ActionType.FULLTEXT, ActionStatus.COMPLETE); + List updatedActions = + this.actionsService.updateActionStatus(siteId, documentId, ActionType.FULLTEXT, status); - this.notificationService.publishNextActionEvent(updatedActions, siteId, documentId); + if (ActionStatus.COMPLETE.equals(status)) { + this.notificationService.publishNextActionEvent(updatedActions, siteId, documentId); + } + + action.status(status); } else if (ActionType.ANTIVIRUS.equals(action.type())) { @@ -485,7 +487,7 @@ private void processAction(final LambdaLogger logger, final String siteId, sendWebhook(siteId, documentId, action); } - logger.log(String.format("Updating Action Status to %s", action.status())); + logAction(logger, "action complete", siteId, documentId, action); } /** @@ -512,12 +514,10 @@ private void processEvent(final LambdaLogger logger, final DocumentEvent event) Action action = o.get(); ActionStatus status = ActionStatus.RUNNING; - logger.log(String.format("Processing SiteId %s Document %s on action %s", siteId, - documentId, action.type())); - this.actionsService.updateActionStatus(siteId, documentId, o.get().type(), status); try { + processAction(logger, siteId, documentId, action); } catch (Exception e) { @@ -562,6 +562,15 @@ private void processRecords(final LambdaLogger logger, final List contentUrls) throws IOException { + private void updateTypesense(final DocumentContentFunction dcFunc, final String siteId, + final String documentId, final Action action, final List contentUrls) + throws IOException { try { @@ -667,7 +680,7 @@ private void updateTypesense(final String siteId, final String documentId, final document.containsKey("text") ? new StringBuilder(document.get("text").toString()) : new StringBuilder(); - String content = getContent(action, contentUrls); + String content = getContent(dcFunc, action, contentUrls); sb.append(" "); sb.append(content); document.put("text", sb.toString()); diff --git a/lambda-s3/src/main/java/com/formkiq/stacks/lambda/s3/DocumentContentFunction.java b/lambda-s3/src/main/java/com/formkiq/stacks/lambda/s3/DocumentContentFunction.java new file mode 100644 index 000000000..b47f6a498 --- /dev/null +++ b/lambda-s3/src/main/java/com/formkiq/stacks/lambda/s3/DocumentContentFunction.java @@ -0,0 +1,165 @@ +/** + * MIT License + * + * Copyright (c) 2018 - 2020 FormKiQ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.formkiq.stacks.lambda.s3; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.net.http.HttpResponse.BodyHandlers; +import java.time.Duration; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import com.formkiq.aws.dynamodb.SiteIdKeyGenerator; +import com.formkiq.aws.dynamodb.model.DocumentItem; +import com.formkiq.aws.dynamodb.objects.MimeType; +import com.formkiq.aws.s3.PresignGetUrlConfig; +import com.formkiq.aws.s3.S3Service; +import com.formkiq.module.lambdaservices.AwsServiceCache; +import com.formkiq.stacks.client.FormKiqClientV1; +import com.formkiq.stacks.client.requests.GetDocumentOcrRequest; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +/** + * + * {@link Function} for getting the Document Content Urls. + * + */ +public class DocumentContentFunction { + + /** S3 Documents Bucket. */ + private String documentsBucket; + /** {@link FormKiqClientV1}. */ + private FormKiqClientV1 formkiqClient; + /** {@link Gson}. */ + private Gson gson = new GsonBuilder().create(); + /** {@link S3Service}. */ + private S3Service s3Service; + + /** + * constructor. + * + * @param serviceCache {@link AwsServiceCache} + */ + public DocumentContentFunction(final AwsServiceCache serviceCache) { + this.s3Service = serviceCache.getExtension(S3Service.class); + this.formkiqClient = serviceCache.getExtension(FormKiqClientV1.class); + this.documentsBucket = serviceCache.environment("DOCUMENTS_S3_BUCKET"); + } + + /** + * Find Content Url. + * + * @param siteId {@link String} + * @param item {@link DocumentItem} + * @return {@link List} {@link String} + * @throws IOException IOException + */ + @SuppressWarnings("unchecked") + private List findContentUrls(final String siteId, final DocumentItem item) + throws IOException { + + List urls = null; + String documentId = item.getDocumentId(); + String s3Key = SiteIdKeyGenerator.createS3Key(siteId, documentId); + + if (MimeType.isPlainText(item.getContentType())) { + + PresignGetUrlConfig config = new PresignGetUrlConfig() + .contentDispositionByPath(item.getPath(), false).contentType(item.getContentType()); + + String url = this.s3Service + .presignGetUrl(this.documentsBucket, s3Key, Duration.ofHours(1), null, config).toString(); + urls = Arrays.asList(url); + + } else { + + GetDocumentOcrRequest req = new GetDocumentOcrRequest().siteId(siteId).documentId(documentId); + + req.addQueryParameter("contentUrl", "true"); + req.addQueryParameter("text", "true"); + + try { + HttpResponse response = this.formkiqClient.getDocumentOcrAsHttpResponse(req); + + Map map = this.gson.fromJson(response.body(), Map.class); + + if (map != null && map.containsKey("contentUrls")) { + urls = (List) map.get("contentUrls"); + } + + } catch (InterruptedException e) { + throw new IOException(e); + } + } + + return urls; + } + + /** + * Get Content from external urls. + * + * @param contentUrls {@link List} {@link String} + * @return {@link StringBuilder} + * @throws IOException IOException + */ + public StringBuilder getContentUrls(final List contentUrls) throws IOException { + + StringBuilder sb = new StringBuilder(); + + try { + for (String contentUrl : contentUrls) { + HttpRequest req = + HttpRequest.newBuilder(new URI(contentUrl)).timeout(Duration.ofMinutes(1)).build(); + HttpResponse response = + HttpClient.newBuilder().build().send(req, BodyHandlers.ofString()); + sb.append(response.body()); + } + } catch (URISyntaxException | InterruptedException e) { + throw new IOException(e); + } + + return sb; + } + + /** + * Convert {@link DocumentItem} to list of Document Content Urls. + * + * @param siteId {@link String} + * @param item {@link DocumentItem} + * @return {@link List} {@link String} + * @throws IOException IOException + */ + public List getContentUrls(final String siteId, final DocumentItem item) + throws IOException { + List contentUrls = findContentUrls(siteId, item); + return contentUrls; + } + +} diff --git a/lambda-s3/src/main/java/com/formkiq/stacks/lambda/s3/DocumentTaggingAction.java b/lambda-s3/src/main/java/com/formkiq/stacks/lambda/s3/DocumentTaggingAction.java new file mode 100644 index 000000000..63c55367f --- /dev/null +++ b/lambda-s3/src/main/java/com/formkiq/stacks/lambda/s3/DocumentTaggingAction.java @@ -0,0 +1,282 @@ +/** + * MIT License + * + * Copyright (c) 2018 - 2020 FormKiQ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.formkiq.stacks.lambda.s3; + +import static com.formkiq.module.http.HttpResponseStatus.is2XX; +import static com.formkiq.stacks.dynamodb.ConfigService.CHATGPT_API_KEY; +import java.io.IOException; +import java.net.http.HttpResponse; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.stream.Collectors; +import java.util.Optional; +import com.amazonaws.services.lambda.runtime.LambdaLogger; +import com.formkiq.aws.dynamodb.DynamicObject; +import com.formkiq.aws.dynamodb.model.DocumentItem; +import com.formkiq.aws.dynamodb.model.DocumentTag; +import com.formkiq.aws.dynamodb.model.DocumentTagType; +import com.formkiq.aws.dynamodb.objects.Strings; +import com.formkiq.module.actions.Action; +import com.formkiq.module.actions.ActionType; +import com.formkiq.module.http.HttpHeaders; +import com.formkiq.module.http.HttpService; +import com.formkiq.module.http.HttpServiceJdk11; +import com.formkiq.module.lambdaservices.AwsServiceCache; +import com.formkiq.stacks.dynamodb.ConfigService; +import com.formkiq.stacks.dynamodb.DocumentService; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonSyntaxException; + +/** + * + * {@link ActionType} Document Tagging implementation of {@link DocumentAction}. + * + */ +public class DocumentTaggingAction implements DocumentAction { + + /** Chat Gpt Frequency Penalty. */ + private static final Double CHAT_GPT_FREQ_PENALTY = Double.valueOf(0.8); + /** Maximum document size to send to ChatGpt. */ + private static final int CHAT_GPT_MAX_LENGTH = 2000; + /** Chat GPT Max Tokens. */ + private static final Integer CHAT_GPT_MAX_TOKENS = Integer.valueOf(1000); + /** Chat Gpt Presence Penalty. */ + private static final Double CHAT_GPT_PRESENCE_PENALTY = Double.valueOf(0.0); + /** Chat Gpt Temperature. */ + private static final Double CHAT_GPT_TEMPERATURE = Double.valueOf(0.5); + /** Chat Gpt Top P. */ + private static final Double CHAT_GPT_TOP_P = Double.valueOf(1.0); + /** {@link ConfigService}. */ + private ConfigService configsService; + /** {@link DocumentService}. */ + private DocumentService documentService; + /** {@link Gson}. */ + private Gson gson = new GsonBuilder().create(); + /** {@link HttpService}. */ + private HttpService http = new HttpServiceJdk11(); + /** {@link AwsServiceCache}. */ + private AwsServiceCache serviceCache; + + /** + * constructor. + * + * @param services {@link AwsServiceCache} + */ + public DocumentTaggingAction(final AwsServiceCache services) { + this.serviceCache = services; + this.configsService = services.getExtension(ConfigService.class); + this.documentService = services.getExtension(DocumentService.class); + } + + private String createChatGptPrompt(final String siteId, final String documentId, + final Action action) throws IOException { + + String tags = getTags(action); + + DocumentItem item = this.documentService.findDocument(siteId, documentId); + + DocumentContentFunction docContentFucn = new DocumentContentFunction(this.serviceCache); + List contentUrls = docContentFucn.getContentUrls(siteId, item); + + if (contentUrls.isEmpty()) { + throw new IOException("'contentUrls' is empty"); + } + + StringBuilder sb = docContentFucn.getContentUrls(contentUrls); + + String text = sb.toString().trim(); + if (text.length() > CHAT_GPT_MAX_LENGTH) { + text = text.substring(0, CHAT_GPT_MAX_LENGTH); + } + + String prompt = "Extract the tags " + tags + " from the text below in JSON format.\n\n" + text; + + return prompt; + } + + private String getTags(final Action action) throws IOException { + String tags = action.parameters().get("tags"); + if (Strings.isEmpty(tags)) { + throw new IOException("missing 'tags' parameter"); + } + return tags; + } + + private List getTagsAsList(final Action action) throws IOException { + return Arrays.asList(getTags(action).split(",")); + } + + @SuppressWarnings("unchecked") + private void parseChatGptResponse(final String siteId, final String documentId, + final Action action, final HttpResponse response) throws IOException { + + List paramTags = getTagsAsList(action); + + Map responseMap = this.gson.fromJson(response.body(), Map.class); + List> choices = (List>) responseMap.get("choices"); + + Collection tags = new ArrayList<>(); + + for (Map choice : choices) { + + String text = choice.get("text").toString(); + Map data = parseGptText(paramTags, text); + + for (Entry e : data.entrySet()) { + + List list = Collections.emptyList(); + + Object obj = e.getValue(); + if (obj != null) { + if (obj instanceof Collection) { + list = (List) obj; + } else { + list = Arrays.asList(obj.toString()); + } + } + + if (!list.isEmpty()) { + + DocumentTag tag = new DocumentTag(documentId, e.getKey(), list.get(0).toString(), + new Date(), "System", DocumentTagType.USERDEFINED); + + if (list.size() > 1) { + tag.setValue(null); + tag.setValues(list.stream().map(i -> i.toString()).collect(Collectors.toList())); + } + + tags.add(tag); + } + } + } + + if (!tags.isEmpty()) { + this.documentService.addTags(siteId, documentId, tags, null); + this.documentService.deleteDocumentTag(siteId, documentId, "untagged"); + } + } + + @SuppressWarnings("unchecked") + private Map parseGptText(final List tags, final String text) { + + Map tagMap = tags.stream().filter(t -> !t.toLowerCase().equals(t)) + .collect(Collectors.toMap(String::toLowerCase, s -> s)); + + Map data = new HashMap<>(); + + try { + data = this.gson.fromJson(text, Map.class); + } catch (JsonSyntaxException e) { + + String[] strs = text.split("\n"); + + for (String s : strs) { + + int pos = s.indexOf(":"); + + if (pos > -1) { + String key = s.substring(0, pos).trim().toLowerCase(); + String value = s.substring(pos + 1).trim(); + + if (!key.isEmpty() && !value.isEmpty()) { + data.put(key, Arrays.asList(value)); + } + } + } + } + + for (Map.Entry e : tagMap.entrySet()) { + Object object = data.get(e.getKey()); + data.put(e.getValue(), object); + data.remove(e.getKey()); + } + + return data; + } + + @Override + public void run(final LambdaLogger logger, final String siteId, final String documentId, + final Action action) throws IOException { + + String engine = action.parameters().get("engine"); + if (engine != null && "chatgpt".equals(engine.toLowerCase())) { + runChatGpt(logger, siteId, documentId, action); + } else { + throw new IOException("Unknown engine: " + engine); + } + } + + /** + * Run ChatGpt Document Tagging. + * + * @param logger {@link LambdaLogger} + * @param siteId {@link String} + * @param documentId {@link String} + * @param action {@link Action} + * @throws IOException IOException + */ + private void runChatGpt(final LambdaLogger logger, final String siteId, final String documentId, + final Action action) throws IOException { + + DynamicObject configs = this.configsService.get(siteId); + String chatGptApiKey = configs.getString(CHATGPT_API_KEY); + + if (chatGptApiKey == null) { + throw new IOException(String.format("missing config '%s'", CHATGPT_API_KEY)); + } + + Optional headers = Optional.of(new HttpHeaders() + .add("Content-Type", "application/json").add("Authorization", "Bearer " + chatGptApiKey)); + + Map payload = new HashMap<>(); + + payload.put("model", "text-davinci-003"); + payload.put("max_tokens", CHAT_GPT_MAX_TOKENS); + payload.put("temperature", CHAT_GPT_TEMPERATURE); + payload.put("top_p", CHAT_GPT_TOP_P); + payload.put("frequency_penalty", CHAT_GPT_FREQ_PENALTY); + payload.put("presence_penalty", CHAT_GPT_PRESENCE_PENALTY); + payload.put("prompt", createChatGptPrompt(siteId, documentId, action)); + + String url = this.serviceCache.environment("CHATGPT_API_COMPLETIONS_URL"); + HttpResponse response = this.http.post(url, headers, this.gson.toJson(payload)); + logger.log(String.format("{\"engine\":\"%s\",\"statusCode\":\"%s\",\"body\":\"%s\"}", "chatgpt", + String.valueOf(response.statusCode()), response.body())); + + if (is2XX(response)) { + parseChatGptResponse(siteId, documentId, action, response); + } else { + throw new IOException("ChatGpt status " + response.statusCode()); + } + } + +} diff --git a/lambda-s3/src/main/java/com/formkiq/stacks/lambda/s3/DocumentsS3Update.java b/lambda-s3/src/main/java/com/formkiq/stacks/lambda/s3/DocumentsS3Update.java index bed6302d1..286d18d23 100644 --- a/lambda-s3/src/main/java/com/formkiq/stacks/lambda/s3/DocumentsS3Update.java +++ b/lambda-s3/src/main/java/com/formkiq/stacks/lambda/s3/DocumentsS3Update.java @@ -50,6 +50,8 @@ import com.formkiq.aws.dynamodb.model.DocumentTag; import com.formkiq.aws.dynamodb.model.DocumentTagType; import com.formkiq.aws.dynamodb.model.DynamicDocumentItem; +import com.formkiq.aws.dynamodb.objects.DateUtil; +import com.formkiq.aws.dynamodb.objects.MimeType; import com.formkiq.aws.s3.S3ConnectionBuilder; import com.formkiq.aws.s3.S3ObjectMetadata; import com.formkiq.aws.s3.S3Service; @@ -72,8 +74,6 @@ import com.formkiq.stacks.client.FormKiqClientV1; import com.formkiq.stacks.client.requests.DeleteDocumentFulltextRequest; import com.formkiq.stacks.client.requests.DeleteDocumentOcrRequest; -import com.formkiq.stacks.common.formats.MimeType; -import com.formkiq.stacks.dynamodb.DateUtil; import com.formkiq.stacks.dynamodb.DocumentService; import com.formkiq.stacks.dynamodb.DocumentServiceImpl; import com.formkiq.stacks.dynamodb.DocumentVersionService; @@ -163,10 +163,14 @@ public static String urlDecode(final String value) { /** constructor. */ public DocumentsS3Update() { this(System.getenv(), EnvironmentVariableCredentialsProvider.create().resolveCredentials(), - new DynamoDbConnectionBuilder().setRegion(Region.of(System.getenv("AWS_REGION"))), - new S3ConnectionBuilder().setRegion(Region.of(System.getenv("AWS_REGION"))), - new SsmConnectionBuilder().setRegion(Region.of(System.getenv("AWS_REGION"))), - new SnsConnectionBuilder().setRegion(Region.of(System.getenv("AWS_REGION")))); + new DynamoDbConnectionBuilder("true".equals(System.getenv("ENABLE_AWS_X_RAY"))) + .setRegion(Region.of(System.getenv("AWS_REGION"))), + new S3ConnectionBuilder("true".equals(System.getenv("ENABLE_AWS_X_RAY"))) + .setRegion(Region.of(System.getenv("AWS_REGION"))), + new SsmConnectionBuilder("true".equals(System.getenv("ENABLE_AWS_X_RAY"))) + .setRegion(Region.of(System.getenv("AWS_REGION"))), + new SnsConnectionBuilder("true".equals(System.getenv("ENABLE_AWS_X_RAY"))) + .setRegion(Region.of(System.getenv("AWS_REGION")))); } /** @@ -298,6 +302,10 @@ public Void handleRequest(final Map map, final Context context) Object bucket = e.getOrDefault("s3bucket", null); Object key = e.getOrDefault("s3key", null); + String s = String.format("{\"eventName\": \"%s\",\"bucket\": \"%s\",\"key\": \"%s\"}", + eventName, bucket, key); + logger.log(s); + if (bucket != null && key != null) { boolean create = @@ -434,39 +442,44 @@ private List> processRecords(final LambdaLogger logger, private void processS3Delete(final LambdaLogger logger, final String bucket, final String key) throws IOException, InterruptedException { - String siteId = getSiteId(key.toString()); - String documentId = resetDatabaseKey(siteId, key.toString()); + if (!this.s3service.getObjectMetadata(bucket, key, null).isObjectExists()) { - String msg = String.format("Removing %s from bucket %s.", key, bucket); - logger.log(msg); + String siteId = getSiteId(key.toString()); + String documentId = resetDatabaseKey(siteId, key.toString()); - boolean moduleOcr = this.services.hasModule("ocr"); - boolean moduleFulltext = this.services.hasModule("fulltext"); + String msg = String.format("Removing %s from bucket %s.", key, bucket); + logger.log(msg); - if (moduleOcr || moduleFulltext) { - FormKiqClientV1 fkClient = this.services.getExtension(FormKiqClientV1.class); + boolean moduleOcr = this.services.hasModule("ocr"); + boolean moduleFulltext = this.services.hasModule("fulltext"); - if (moduleOcr) { - HttpResponse deleteDocumentHttpResponse = fkClient.deleteDocumentOcrAsHttpResponse( - new DeleteDocumentOcrRequest().siteId(siteId).documentId(documentId)); - checkResponse("ocr", siteId, documentId, deleteDocumentHttpResponse); - } + if (moduleOcr || moduleFulltext) { + FormKiqClientV1 fkClient = this.services.getExtension(FormKiqClientV1.class); - if (moduleFulltext) { - HttpResponse deleteDocumentFulltext = fkClient.deleteDocumentFulltextAsHttpResponse( - new DeleteDocumentFulltextRequest().siteId(siteId).documentId(documentId)); - checkResponse("fulltext", siteId, documentId, deleteDocumentFulltext); + if (moduleOcr) { + HttpResponse deleteDocumentHttpResponse = + fkClient.deleteDocumentOcrAsHttpResponse( + new DeleteDocumentOcrRequest().siteId(siteId).documentId(documentId)); + checkResponse("ocr", siteId, documentId, deleteDocumentHttpResponse); + } + + if (moduleFulltext) { + HttpResponse deleteDocumentFulltext = + fkClient.deleteDocumentFulltextAsHttpResponse( + new DeleteDocumentFulltextRequest().siteId(siteId).documentId(documentId)); + checkResponse("fulltext", siteId, documentId, deleteDocumentFulltext); + } } - } - this.service.deleteDocument(siteId, documentId); + this.service.deleteDocument(siteId, documentId); - DynamicDocumentItem doc = new DynamicDocumentItem(Map.of("documentId", documentId)); + DynamicDocumentItem doc = new DynamicDocumentItem(Map.of("documentId", documentId)); - DocumentEvent event = - buildDocumentEvent(DELETE, siteId, doc, bucket, key, doc.getContentType()); + DocumentEvent event = + buildDocumentEvent(DELETE, siteId, doc, bucket, key, doc.getContentType()); - sendSnsMessage(logger, event, doc.getContentType(), bucket, key, null); + sendSnsMessage(logger, event, doc.getContentType(), bucket, key, null); + } } /** diff --git a/lambda-s3/src/main/java/com/formkiq/stacks/lambda/s3/StagingS3Create.java b/lambda-s3/src/main/java/com/formkiq/stacks/lambda/s3/StagingS3Create.java index 9b15ab56c..14f4d8166 100644 --- a/lambda-s3/src/main/java/com/formkiq/stacks/lambda/s3/StagingS3Create.java +++ b/lambda-s3/src/main/java/com/formkiq/stacks/lambda/s3/StagingS3Create.java @@ -61,7 +61,6 @@ import com.formkiq.aws.ssm.SsmServiceCache; import com.formkiq.graalvm.annotations.Reflectable; import com.formkiq.graalvm.annotations.ReflectableClass; -import com.formkiq.graalvm.annotations.ReflectableClasses; import com.formkiq.graalvm.annotations.ReflectableField; import com.formkiq.graalvm.annotations.ReflectableImport; import com.formkiq.module.actions.Action; @@ -104,14 +103,13 @@ @ReflectableImport(classes = {DocumentItemDynamoDb.class, DocumentTag.class, DocumentMetadata.class, DocumentTagType.class, AddDocumentTag.class, DocumentVersionServiceDynamoDb.class, DocumentVersionServiceNoVersioning.class}) -@ReflectableClasses({ - @ReflectableClass(className = AddDocumentTagRequest.class, allPublicConstructors = true, - fields = {@ReflectableField(name = "tag"), @ReflectableField(name = "tags")}), - @ReflectableClass(className = AddDocumentTagRequest.class, allPublicConstructors = true, - fields = {@ReflectableField(name = "tag"), @ReflectableField(name = "tags")}), - @ReflectableClass(className = AddDocumentTag.class, allPublicConstructors = true, - fields = {@ReflectableField(name = "key"), @ReflectableField(name = "value"), - @ReflectableField(name = "values")})}) +@ReflectableClass(className = AddDocumentTagRequest.class, allPublicConstructors = true, + fields = {@ReflectableField(name = "tag"), @ReflectableField(name = "tags")}) +@ReflectableClass(className = AddDocumentTagRequest.class, allPublicConstructors = true, + fields = {@ReflectableField(name = "tag"), @ReflectableField(name = "tags")}) +@ReflectableClass(className = AddDocumentTag.class, allPublicConstructors = true, + fields = {@ReflectableField(name = "key"), @ReflectableField(name = "value"), + @ReflectableField(name = "values")}) public class StagingS3Create implements RequestHandler, Void> { /** Extension for FormKiQ config file. */ @@ -209,10 +207,14 @@ private static String urlDecode(final String value) { */ public StagingS3Create() { this(System.getenv(), EnvironmentVariableCredentialsProvider.create().resolveCredentials(), - new DynamoDbConnectionBuilder().setRegion(Region.of(System.getenv("AWS_REGION"))), - new S3ConnectionBuilder().setRegion(Region.of(System.getenv("AWS_REGION"))), - new SsmConnectionBuilder().setRegion(Region.of(System.getenv("AWS_REGION"))), - new SnsConnectionBuilder().setRegion(Region.of(System.getenv("AWS_REGION")))); + new DynamoDbConnectionBuilder("true".equals(System.getenv("ENABLE_AWS_X_RAY"))) + .setRegion(Region.of(System.getenv("AWS_REGION"))), + new S3ConnectionBuilder("true".equals(System.getenv("ENABLE_AWS_X_RAY"))) + .setRegion(Region.of(System.getenv("AWS_REGION"))), + new SsmConnectionBuilder("true".equals(System.getenv("ENABLE_AWS_X_RAY"))) + .setRegion(Region.of(System.getenv("AWS_REGION"))), + new SnsConnectionBuilder("true".equals(System.getenv("ENABLE_AWS_X_RAY"))) + .setRegion(Region.of(System.getenv("AWS_REGION")))); } /** diff --git a/lambda-s3/src/main/resources/cloudformation/template-sar.yaml b/lambda-s3/src/main/resources/cloudformation/template-sar.yaml index 75209413c..eb2f8fa51 100644 --- a/lambda-s3/src/main/resources/cloudformation/template-sar.yaml +++ b/lambda-s3/src/main/resources/cloudformation/template-sar.yaml @@ -220,13 +220,14 @@ Resources: Type: AWS::Logs::LogGroup Properties: LogGroupName: - Fn::Sub: "/${AWS::StackName}/${DocumentActionsProcessor}" + Fn::Sub: "/aws/vendedlogs/${AWS::StackName}/${DocumentActionsProcessor}" RetentionInDays: 90 DocumentActionsProcessor: Type: AWS::Serverless::Function DependsOn: - S3LambdaRolePolicy + - S3LambdaRole Properties: Handler: com.formkiq.stacks.lambda.s3.DocumentActionsProcessor Description: Lambda function to process Document Actions requests @@ -236,14 +237,18 @@ Resources: MemorySize: Fn::Sub: "${LambdaMemory}" CodeUri: ./lambda-s3-graalvm.zip + Tracing: Active AutoPublishCodeSha256: #@ data.values.hash or assert.fail("missing version") Environment: Variables: + DOCUMENTS_S3_BUCKET: + Fn::Sub: "formkiq-${FormKiQType}-${AppEnvironment}-documents-${AWS::AccountId}" DOCUMENTS_TABLE: Ref: Documents APP_ENVIRONMENT: Fn::Sub: "${AppEnvironment}" - DEBUG: false + DEBUG: false + ENABLE_AWS_X_RAY: true FormKiQType: Ref: FormKiQType SNS_DOCUMENT_EVENT: @@ -251,6 +256,7 @@ Resources: DOCUMENT_VERSIONS_PLUGIN: "com.formkiq.stacks.dynamodb.DocumentVersionServiceNoVersioning" DOCUMENT_SYNC_TABLE: Ref: DocumentSyncs + CHATGPT_API_COMPLETIONS_URL: "https://api.openai.com/v1/completions" Role: Fn::GetAtt: - S3LambdaRole @@ -276,13 +282,14 @@ Resources: Type: AWS::Logs::LogGroup Properties: LogGroupName: - Fn::Sub: "/${AWS::StackName}/${StagingS3Create}" + Fn::Sub: "/aws/vendedlogs/${AWS::StackName}/${StagingS3Create}" RetentionInDays: 90 StagingS3Create: Type: AWS::Serverless::Function DependsOn: - S3LambdaRolePolicy + - S3LambdaRole Properties: Handler: com.formkiq.stacks.lambda.s3.StagingS3Create Description: Lambda function that processes document written to the Staging S3 Bucket @@ -292,6 +299,7 @@ Resources: MemorySize: Fn::Sub: "${LambdaMemory}" CodeUri: ./lambda-s3-graalvm.zip + Tracing: Active AutoPublishCodeSha256: #@ data.values.hash or assert.fail("missing version") Environment: Variables: @@ -302,6 +310,7 @@ Resources: APP_ENVIRONMENT: Fn::Sub: "${AppEnvironment}" DEBUG: false + ENABLE_AWS_X_RAY: true FormKiQType: Ref: FormKiQType SNS_DOCUMENT_EVENT: @@ -361,13 +370,14 @@ Resources: Type: AWS::Logs::LogGroup Properties: LogGroupName: - Fn::Sub: "/${AWS::StackName}/${DocumentsS3Update}" + Fn::Sub: "/aws/vendedlogs/${AWS::StackName}/${DocumentsS3Update}" RetentionInDays: 90 DocumentsS3Update: Type: AWS::Serverless::Function DependsOn: - S3LambdaRolePolicy + - S3LambdaRole Properties: Handler: com.formkiq.stacks.lambda.s3.DocumentsS3Update Description: Lambda function that processes document written to the Documents S3 Bucket @@ -377,6 +387,7 @@ Resources: MemorySize: Fn::Sub: "${LambdaMemory}" CodeUri: ./lambda-s3-graalvm.zip + Tracing: Active AutoPublishCodeSha256: #@ data.values.hash or assert.fail("missing version") Environment: Variables: @@ -385,6 +396,7 @@ Resources: APP_ENVIRONMENT: Fn::Sub: "${AppEnvironment}" DEBUG: false + ENABLE_AWS_X_RAY: true SNS_DOCUMENT_EVENT: Ref: SnsDocumentEvent FormKiQType: @@ -602,6 +614,8 @@ Resources: Action: - sts:AssumeRole Path: / + ManagedPolicyArns: + - arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess S3LambdaRolePolicy: Type: "AWS::IAM::Policy" diff --git a/lambda-s3/src/test/java/com/formkiq/stacks/lambda/s3/DocumentActionsProcessorTest.java b/lambda-s3/src/test/java/com/formkiq/stacks/lambda/s3/DocumentActionsProcessorTest.java index 034ecfacd..a41156c12 100644 --- a/lambda-s3/src/test/java/com/formkiq/stacks/lambda/s3/DocumentActionsProcessorTest.java +++ b/lambda-s3/src/test/java/com/formkiq/stacks/lambda/s3/DocumentActionsProcessorTest.java @@ -23,15 +23,17 @@ */ package com.formkiq.stacks.lambda.s3; +import static com.formkiq.stacks.dynamodb.ConfigService.CHATGPT_API_KEY; +import static com.formkiq.stacks.dynamodb.DocumentService.MAX_RESULTS; import static com.formkiq.stacks.lambda.s3.util.FileUtils.loadFileAsMap; import static com.formkiq.testutils.aws.DynamoDbExtension.DOCUMENTS_TABLE; import static com.formkiq.testutils.aws.DynamoDbExtension.DOCUMENTS_VERSION_TABLE; import static com.formkiq.testutils.aws.TestServices.BUCKET_NAME; import static com.formkiq.testutils.aws.TypeSenseExtension.API_KEY; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockserver.integration.ClientAndServer.startClientAndServer; import static org.mockserver.model.HttpRequest.request; import java.io.IOException; @@ -54,7 +56,9 @@ import org.mockserver.integration.ClientAndServer; import org.mockserver.model.HttpRequest; import com.formkiq.aws.dynamodb.DbKeys; +import com.formkiq.aws.dynamodb.DynamicObject; import com.formkiq.aws.dynamodb.DynamoDbConnectionBuilder; +import com.formkiq.aws.dynamodb.PaginationResults; import com.formkiq.aws.dynamodb.SiteIdKeyGenerator; import com.formkiq.aws.dynamodb.model.DocumentItem; import com.formkiq.aws.dynamodb.model.DocumentTag; @@ -70,11 +74,14 @@ import com.formkiq.module.actions.services.ActionsServiceDynamoDb; import com.formkiq.module.typesense.TypeSenseService; import com.formkiq.module.typesense.TypeSenseServiceImpl; +import com.formkiq.stacks.dynamodb.ConfigService; +import com.formkiq.stacks.dynamodb.ConfigServiceDynamoDb; import com.formkiq.stacks.dynamodb.DocumentItemDynamoDb; import com.formkiq.stacks.dynamodb.DocumentService; import com.formkiq.stacks.dynamodb.DocumentServiceImpl; import com.formkiq.stacks.dynamodb.DocumentVersionService; import com.formkiq.stacks.dynamodb.DocumentVersionServiceNoVersioning; +import com.formkiq.stacks.lambda.s3.util.FileUtils; import com.formkiq.stacks.lambda.s3.util.LambdaContextRecorder; import com.formkiq.testutils.aws.DynamoDbExtension; import com.formkiq.testutils.aws.DynamoDbTestServices; @@ -100,14 +107,14 @@ public class DocumentActionsProcessorTest implements DbKeys { /** {@link RequestRecordExpectationResponseCallback}. */ private static RequestRecordExpectationResponseCallback callback = new RequestRecordExpectationResponseCallback(); + /** {@link ConfigService}. */ + private static ConfigService configService; /** {@link AwsBasicCredentials}. */ private static AwsBasicCredentials credentials = AwsBasicCredentials.create("asd", "asd"); /** {@link DynamoDbConnectionBuilder}. */ private static DynamoDbConnectionBuilder dbBuilder; - /** {@link DocumentService}. */ private static DocumentService documentService; - /** {@link Gson}. */ private static Gson gson = new GsonBuilder().disableHtmlEscaping().create(); @@ -156,8 +163,6 @@ public static void beforeClass() throws Exception { ssmService = new SsmServiceCache(ssmBuilder, 1, TimeUnit.DAYS); ssmService.putParameter("/formkiq/" + APP_ENVIRONMENT + "/api/DocumentsIamUrl", URL); - ssmService.putParameter("/formkiq/" + APP_ENVIRONMENT + "/s3/DocumentsS3Bucket", BUCKET_NAME); - ssmService.putParameter("/formkiq/" + APP_ENVIRONMENT + "/s3/OcrBucket", BUCKET_NAME); String typeSenseHost = "http://localhost:" + TypeSenseExtension.getMappedPort(); ssmService.putParameter("/formkiq/" + APP_ENVIRONMENT + "/api/TypesenseEndpoint", @@ -165,28 +170,43 @@ public static void beforeClass() throws Exception { ssmService.putParameter("/formkiq/" + APP_ENVIRONMENT + "/typesense/ApiKey", API_KEY); typesense = new TypeSenseServiceImpl(typeSenseHost, API_KEY, Region.US_EAST_1, credentials); + + configService = new ConfigServiceDynamoDb(dbBuilder, DOCUMENTS_TABLE); } /** * Create Mock Server. + * + * @throws IOException IOException */ - private static void createMockServer() { + private static void createMockServer() throws IOException { mockServer = startClientAndServer(Integer.valueOf(PORT)); + final int status = 200; + + for (String item : Arrays.asList("1", "2", "3")) { + String text = FileUtils.loadFile(mockServer, "/chatgpt/response" + item + ".json"); + mockServer.when(request().withMethod("POST").withPath("/chatgpt" + item)).respond( + org.mockserver.model.HttpResponse.response(text).withStatusCode(Integer.valueOf(status))); + } + mockServer.when(request().withMethod("PATCH")).respond(callback); mockServer.when(request().withMethod("POST")).respond(callback); mockServer.when(request().withMethod("PUT")).respond(callback); mockServer.when(request().withMethod("GET")).respond(callback); } - private static void initProcessor(final String module) throws URISyntaxException { + private static void initProcessor(final String module, final String chatgptUrl) + throws URISyntaxException { Map env = new HashMap<>(); env.put("DOCUMENTS_TABLE", DOCUMENTS_TABLE); env.put("DOCUMENT_VERSIONS_TABLE", DOCUMENTS_VERSION_TABLE); env.put("APP_ENVIRONMENT", APP_ENVIRONMENT); + env.put("DOCUMENTS_S3_BUCKET", BUCKET_NAME); env.put("MODULE_" + module, "true"); env.put("DOCUMENT_VERSIONS_PLUGIN", DocumentVersionServiceNoVersioning.class.getName()); + env.put("CHATGPT_API_COMPLETIONS_URL", URL + "/" + chatgptUrl); processor = new DocumentActionsProcessor(env, Region.US_EAST_1, credentials, dbBuilder, TestServices.getS3Connection(null), TestServices.getSsmConnection(null), @@ -206,7 +226,269 @@ public void beforeEach() throws Exception { this.context = new LambdaContextRecorder(); callback.reset(); - initProcessor("fulltext"); + initProcessor("fulltext", "chatgpt1"); + } + + /** + * Handle documentTagging ChatApt Action missing GptKey. + * + * @throws Exception Exception + */ + @Test + public void testDocumentTaggingAction01() throws Exception { + for (String siteId : Arrays.asList(null, UUID.randomUUID().toString())) { + // given + String documentId = UUID.randomUUID().toString(); + List actions = + Arrays.asList(new Action().type(ActionType.DOCUMENTTAGGING).parameters(Map.of("engine", + "chatgpt", "tags", "organization,location,person,subject,sentiment,document type"))); + actionsService.saveActions(siteId, documentId, actions); + + Map map = + loadFileAsMap(this, "/actions-event01.json", "c2695f67-d95e-4db0-985e-574168b12e57", + documentId, "default", siteId != null ? siteId : "default"); + + // when + processor.handleRequest(map, this.context); + + // then + assertEquals(ActionStatus.FAILED, + actionsService.getActions(siteId, documentId).get(0).status()); + } + } + + /** + * Handle documentTagging ChatApt Action. + * + * @throws Exception Exception + */ + @Test + public void testDocumentTaggingAction02() throws Exception { + + for (String siteId : Arrays.asList(null, UUID.randomUUID().toString())) { + // given + configService.save(siteId, new DynamicObject(Map.of(CHATGPT_API_KEY, "asd"))); + + String documentId = UUID.randomUUID().toString(); + + DocumentItem item = new DocumentItemDynamoDb(documentId, new Date(), "joe"); + item.setContentType("text/plain"); + + String s3Key = SiteIdKeyGenerator.createS3Key(siteId, documentId); + String content = "this is some data"; + s3Service.putObject(BUCKET_NAME, s3Key, content.getBytes(StandardCharsets.UTF_8), + "text/plain"); + + documentService.saveDocument(siteId, item, null); + documentService.addTags(siteId, documentId, Arrays.asList(new DocumentTag(documentId, + "untagged", "", new Date(), "joe", DocumentTagType.SYSTEMDEFINED)), null); + + List actions = + Arrays.asList(new Action().type(ActionType.DOCUMENTTAGGING).parameters(Map.of("engine", + "chatgpt", "tags", "organization,location,person,subject,sentiment,document type"))); + actionsService.saveActions(siteId, documentId, actions); + + Map map = + loadFileAsMap(this, "/actions-event01.json", "c2695f67-d95e-4db0-985e-574168b12e57", + documentId, "default", siteId != null ? siteId : "default"); + + // when + processor.handleRequest(map, this.context); + + // then + final int expectedSize = 5; + assertEquals(ActionStatus.COMPLETE, + actionsService.getActions(siteId, documentId).get(0).status()); + + PaginationResults tags = + documentService.findDocumentTags(siteId, documentId, null, MAX_RESULTS); + assertEquals(expectedSize, tags.getResults().size()); + + int i = 0; + assertEquals("Document Type", tags.getResults().get(i).getKey()); + assertEquals("Memorandum", tags.getResults().get(i++).getValue()); + + assertEquals("Location", tags.getResults().get(i).getKey()); + assertEquals("YellowBelly Brewery Pub, St. Johns, NL", tags.getResults().get(i++).getValue()); + + assertEquals("Organization", tags.getResults().get(i).getKey()); + assertEquals("Great Auk Enterprises", tags.getResults().get(i++).getValue()); + + assertEquals("Person", tags.getResults().get(i).getKey()); + assertEquals("Thomas Bewick,Ketill Ketilsson,Farley Mowat,Aaron Thomas", + String.join(",", tags.getResults().get(i++).getValues())); + + assertEquals("Subject", tags.getResults().get(i).getKey()); + assertEquals("MINUTES OF A MEETING OF DIRECTORS", tags.getResults().get(i++).getValue()); + } + } + + /** + * Handle documentTagging invalid engine. + * + * @throws Exception Exception + */ + @Test + public void testDocumentTaggingAction03() throws Exception { + for (String siteId : Arrays.asList(null, UUID.randomUUID().toString())) { + // given + String documentId = UUID.randomUUID().toString(); + List actions = + Arrays.asList(new Action().type(ActionType.DOCUMENTTAGGING).parameters(Map.of("engine", + "unknown", "tags", "organization,location,person,subject,sentiment,document type"))); + actionsService.saveActions(siteId, documentId, actions); + + Map map = + loadFileAsMap(this, "/actions-event01.json", "c2695f67-d95e-4db0-985e-574168b12e57", + documentId, "default", siteId != null ? siteId : "default"); + + // when + processor.handleRequest(map, this.context); + + // then + assertEquals(ActionStatus.FAILED, + actionsService.getActions(siteId, documentId).get(0).status()); + } + } + + /** + * Handle documentTagging ChatApt Action with a non JSON repsonse from ChatGPT. + * + * @throws Exception Exception + */ + @Test + public void testDocumentTaggingAction04() throws Exception { + + initProcessor("fulltext", "chatgpt2"); + + for (String siteId : Arrays.asList(null, UUID.randomUUID().toString())) { + // given + configService.save(siteId, new DynamicObject(Map.of(CHATGPT_API_KEY, "asd"))); + + String documentId = UUID.randomUUID().toString(); + + DocumentItem item = new DocumentItemDynamoDb(documentId, new Date(), "joe"); + item.setContentType("text/plain"); + + String s3Key = SiteIdKeyGenerator.createS3Key(siteId, documentId); + String content = "this is some data"; + s3Service.putObject(BUCKET_NAME, s3Key, content.getBytes(StandardCharsets.UTF_8), + "text/plain"); + + documentService.saveDocument(siteId, item, null); + documentService.addTags(siteId, documentId, Arrays.asList(new DocumentTag(documentId, + "untagged", "", new Date(), "joe", DocumentTagType.SYSTEMDEFINED)), null); + + List actions = + Arrays.asList(new Action().type(ActionType.DOCUMENTTAGGING).parameters(Map.of("engine", + "chatgpt", "tags", "Organization,location,person,subject,sentiment,document type"))); + actionsService.saveActions(siteId, documentId, actions); + + Map map = + loadFileAsMap(this, "/actions-event01.json", "c2695f67-d95e-4db0-985e-574168b12e57", + documentId, "default", siteId != null ? siteId : "default"); + + // when + processor.handleRequest(map, this.context); + + // then + final int expectedSize = 6; + assertEquals(ActionStatus.COMPLETE, + actionsService.getActions(siteId, documentId).get(0).status()); + + PaginationResults tags = + documentService.findDocumentTags(siteId, documentId, null, MAX_RESULTS); + assertEquals(expectedSize, tags.getResults().size()); + + int i = 0; + assertEquals("Organization", tags.getResults().get(i).getKey()); + assertEquals("East Repair Inc.", tags.getResults().get(i++).getValue()); + + assertEquals("document type", tags.getResults().get(i).getKey()); + assertEquals("Receipt", tags.getResults().get(i++).getValue()); + + assertEquals("location", tags.getResults().get(i).getKey()); + assertEquals("New York, NY 12240; Cambutdigo, MA 12210", + tags.getResults().get(i++).getValue()); + + assertEquals("person", tags.getResults().get(i).getKey()); + assertEquals("Job Smith", tags.getResults().get(i++).getValue()); + + assertEquals("sentiment", tags.getResults().get(i).getKey()); + assertEquals("None", tags.getResults().get(i++).getValue()); + + assertEquals("subject", tags.getResults().get(i).getKey()); + assertEquals("Receipt", tags.getResults().get(i++).getValue()); + } + } + + /** + * Handle documentTagging ChatApt Action with a non JSON repsonse from ChatGPT. + * + * @throws Exception Exception + */ + @Test + public void testDocumentTaggingAction05() throws Exception { + + initProcessor("fulltext", "chatgpt3"); + + for (String siteId : Arrays.asList(null, UUID.randomUUID().toString())) { + // given + configService.save(siteId, new DynamicObject(Map.of(CHATGPT_API_KEY, "asd"))); + + String documentId = UUID.randomUUID().toString(); + + DocumentItem item = new DocumentItemDynamoDb(documentId, new Date(), "joe"); + item.setContentType("text/plain"); + + String s3Key = SiteIdKeyGenerator.createS3Key(siteId, documentId); + String content = "this is some data"; + s3Service.putObject(BUCKET_NAME, s3Key, content.getBytes(StandardCharsets.UTF_8), + "text/plain"); + + documentService.saveDocument(siteId, item, null); + documentService.addTags(siteId, documentId, Arrays.asList(new DocumentTag(documentId, + "untagged", "", new Date(), "joe", DocumentTagType.SYSTEMDEFINED)), null); + + List actions = + Arrays.asList(new Action().type(ActionType.DOCUMENTTAGGING).parameters(Map.of("engine", + "chatgpt", "tags", "Organization,location,person,subject,sentiment,document type"))); + actionsService.saveActions(siteId, documentId, actions); + + Map map = + loadFileAsMap(this, "/actions-event01.json", "c2695f67-d95e-4db0-985e-574168b12e57", + documentId, "default", siteId != null ? siteId : "default"); + + // when + processor.handleRequest(map, this.context); + + // then + final int expectedSize = 5; + assertEquals(ActionStatus.COMPLETE, + actionsService.getActions(siteId, documentId).get(0).status()); + + PaginationResults tags = + documentService.findDocumentTags(siteId, documentId, null, MAX_RESULTS); + assertEquals(expectedSize, tags.getResults().size()); + + int i = 0; + assertEquals("Organization", tags.getResults().get(i).getKey()); + assertEquals("East Repair Inc.", tags.getResults().get(i++).getValue()); + + assertEquals("document type", tags.getResults().get(i).getKey()); + assertEquals("Receipt", tags.getResults().get(i++).getValue()); + + assertEquals("location", tags.getResults().get(i).getKey()); + assertEquals("New York, NY 12240,Cambutdigo, MA 12210", + String.join(",", tags.getResults().get(i++).getValues())); + + assertEquals("person", tags.getResults().get(i).getKey()); + assertEquals("Job Smith", tags.getResults().get(i++).getValue()); + + assertEquals("subject", tags.getResults().get(i).getKey()); + assertEquals("Frontend eaar brake cabies,New set of podal arms,Labor shrs 500", + String.join(",", tags.getResults().get(i++).getValues())); + } } /** @@ -498,7 +780,7 @@ public void testHandle06() throws IOException, URISyntaxException { @SuppressWarnings("unchecked") @Test public void testHandle07() throws IOException, URISyntaxException { - initProcessor("typesense"); + initProcessor("typesense", "chatgpt1"); String content = "this is some data"; diff --git a/lambda-s3/src/test/java/com/formkiq/stacks/lambda/s3/DocumentsS3UpdateTest.java b/lambda-s3/src/test/java/com/formkiq/stacks/lambda/s3/DocumentsS3UpdateTest.java index d33312a55..9c4eca4e3 100644 --- a/lambda-s3/src/test/java/com/formkiq/stacks/lambda/s3/DocumentsS3UpdateTest.java +++ b/lambda-s3/src/test/java/com/formkiq/stacks/lambda/s3/DocumentsS3UpdateTest.java @@ -34,11 +34,11 @@ import static com.formkiq.testutils.aws.DynamoDbExtension.DOCUMENTS_TABLE; import static com.formkiq.testutils.aws.DynamoDbExtension.DOCUMENTS_VERSION_TABLE; import static com.formkiq.testutils.aws.TestServices.AWS_REGION; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockserver.integration.ClientAndServer.startClientAndServer; import static org.mockserver.model.HttpRequest.request; import java.io.IOException; @@ -569,7 +569,7 @@ public void testHandleRequest02() throws Exception { } /** - * Delete Document Request - core. + * Delete Document Request - S3 File exists (deleting version document). * * @throws Exception Exception */ @@ -601,6 +601,42 @@ public void testHandleRequest03() throws Exception { // when DocumentItem item = handleRequest(siteId, BUCKET_KEY, map); + // then + assertNotNull(item); + } + } + + /** + * Delete Document Request - S3 File not exists. S3 Main document file was deleted. + * + * @throws Exception Exception + */ + @Test + @Timeout(unit = TimeUnit.MILLISECONDS, value = TEST_TIMEOUT) + public void testHandleRequest04() throws Exception { + + for (String siteId : Arrays.asList(null, UUID.randomUUID().toString())) { + // given + this.logger.reset(); + + String key = createDatabaseKey(siteId, BUCKET_KEY); + final Map map = + loadFileAsMap(this, "/objectremove-event1.json", BUCKET_KEY, key); + + DynamicDocumentItem doc = new DynamicDocumentItem(Map.of()); + doc.setInsertedDate(new Date()); + doc.setDocumentId(BUCKET_KEY); + doc.setPath("test.txt"); + + DynamicDocumentTag tag = new DynamicDocumentTag(Map.of("documentId", BUCKET_KEY, "key", + "person", "value", "category", "insertedDate", new Date(), "userId", "asd")); + doc.put("tags", Arrays.asList(tag)); + + service.saveDocumentItemWithTag(siteId, doc); + + // when + DocumentItem item = handleRequest(siteId, BUCKET_KEY, map); + // then assertNull(item); assertPublishSnsMessage(siteId, sqsDocumentEventUrl, DELETE, false, true); @@ -614,7 +650,7 @@ public void testHandleRequest03() throws Exception { */ @Test @Timeout(unit = TimeUnit.MILLISECONDS, value = TEST_TIMEOUT) - public void testHandleRequest04() throws Exception { + public void testHandleRequest05() throws Exception { String documentId = UUID.randomUUID().toString(); @@ -683,7 +719,7 @@ public void testHandleRequest04() throws Exception { */ @Test @Timeout(unit = TimeUnit.MILLISECONDS, value = TEST_TIMEOUT) - public void testHandleRequest05() throws Exception { + public void testHandleRequest06() throws Exception { for (String siteId : Arrays.asList(null, UUID.randomUUID().toString())) { // given this.logger.reset(); @@ -735,7 +771,7 @@ public void testHandleRequest05() throws Exception { */ @Test @Timeout(unit = TimeUnit.MILLISECONDS, value = TEST_TIMEOUT) - public void testHandleRequest06() throws Exception { + public void testHandleRequest07() throws Exception { for (String siteId : Arrays.asList(null, UUID.randomUUID().toString())) { // given @@ -770,7 +806,7 @@ public void testHandleRequest06() throws Exception { */ @Test @Timeout(unit = TimeUnit.MILLISECONDS, value = TEST_TIMEOUT) - public void testHandleRequest07() throws Exception { + public void testHandleRequest08() throws Exception { for (String siteId : Arrays.asList(null, UUID.randomUUID().toString())) { // given @@ -806,7 +842,7 @@ public void testHandleRequest07() throws Exception { */ @Test @Timeout(unit = TimeUnit.MILLISECONDS, value = TEST_TIMEOUT) - public void testHandleRequest08() throws Exception { + public void testHandleRequest09() throws Exception { for (String siteId : Arrays.asList(null, UUID.randomUUID().toString())) { // given @@ -842,7 +878,7 @@ public void testHandleRequest08() throws Exception { */ @Test @Timeout(unit = TimeUnit.MILLISECONDS, value = TEST_TIMEOUT) - public void testHandleRequest09() throws Exception { + public void testHandleRequest10() throws Exception { String ttl = "1612061365"; for (String siteId : Arrays.asList(null, UUID.randomUUID().toString())) { // given @@ -892,7 +928,7 @@ public void testHandleRequest09() throws Exception { */ @Test @Timeout(unit = TimeUnit.MILLISECONDS, value = TEST_TIMEOUT) - public void testHandleRequest10() throws Exception { + public void testHandleRequest11() throws Exception { for (String siteId : Arrays.asList(null, UUID.randomUUID().toString())) { // given @@ -929,7 +965,7 @@ public void testHandleRequest10() throws Exception { */ @Test @Timeout(unit = TimeUnit.MILLISECONDS, value = TEST_TIMEOUT) - public void testHandleRequest11() throws Exception { + public void testHandleRequest12() throws Exception { createMockServer(DocumentsS3Update.SERVER_ERROR); this.modules = Arrays.asList("ocr", "fulltext"); @@ -968,7 +1004,7 @@ public void testHandleRequest11() throws Exception { */ @Test @Timeout(unit = TimeUnit.MILLISECONDS, value = TEST_TIMEOUT) - public void testHandleRequest12() throws Exception { + public void testHandleRequest13() throws Exception { createMockServer(OK); this.modules = Arrays.asList("ocr", "fulltext"); @@ -1002,7 +1038,7 @@ public void testHandleRequest12() throws Exception { */ @Test @Timeout(unit = TimeUnit.MILLISECONDS, value = TEST_TIMEOUT) - public void testHandleRequest13() throws Exception { + public void testHandleRequest14() throws Exception { for (String siteId : Arrays.asList(null, UUID.randomUUID().toString())) { // given diff --git a/lambda-s3/src/test/java/com/formkiq/stacks/lambda/s3/StagingS3CreateTest.java b/lambda-s3/src/test/java/com/formkiq/stacks/lambda/s3/StagingS3CreateTest.java index 557e8fd58..4843362d5 100644 --- a/lambda-s3/src/test/java/com/formkiq/stacks/lambda/s3/StagingS3CreateTest.java +++ b/lambda-s3/src/test/java/com/formkiq/stacks/lambda/s3/StagingS3CreateTest.java @@ -36,12 +36,12 @@ import static com.formkiq.testutils.aws.DynamoDbExtension.DOCUMENTS_VERSION_TABLE; import static com.formkiq.testutils.aws.DynamoDbExtension.DOCUMENT_SYNCS_TABLE; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockserver.integration.ClientAndServer.startClientAndServer; import static org.mockserver.model.HttpRequest.request; import java.io.IOException; @@ -86,6 +86,7 @@ import com.formkiq.aws.dynamodb.model.DocumentTag; import com.formkiq.aws.dynamodb.model.DocumentTagType; import com.formkiq.aws.dynamodb.model.DynamicDocumentItem; +import com.formkiq.aws.dynamodb.objects.DateUtil; import com.formkiq.aws.s3.S3ConnectionBuilder; import com.formkiq.aws.s3.S3ObjectMetadata; import com.formkiq.aws.s3.S3Service; @@ -102,7 +103,6 @@ import com.formkiq.module.actions.services.ActionsService; import com.formkiq.module.actions.services.ActionsServiceDynamoDb; import com.formkiq.module.documentevents.DocumentEvent; -import com.formkiq.stacks.dynamodb.DateUtil; import com.formkiq.stacks.dynamodb.DocumentItemDynamoDb; import com.formkiq.stacks.dynamodb.DocumentService; import com.formkiq.stacks.dynamodb.DocumentServiceImpl; diff --git a/lambda-s3/src/test/resources/chatgpt/response1.json b/lambda-s3/src/test/resources/chatgpt/response1.json new file mode 100644 index 000000000..427f7b6db --- /dev/null +++ b/lambda-s3/src/test/resources/chatgpt/response1.json @@ -0,0 +1,22 @@ +{ + "id": "cmpl-7HOmfBlUrif1MEDOLEFEtJ44A9iGq", + "object": "text_completion", + "created": 1684381201, + "model": "text-davinci-003", + "choices": + [ + { + "text": "\n\n{\n \"Organization\": [\"Great Auk Enterprises\"],\n \"Location\": [\"YellowBelly Brewery Pub, St. Johns, NL\"],\n \"Person\": [\"Thomas Bewick\", \"Ketill Ketilsson\", \"Farley Mowat\", \"Aaron Thomas\"], \n \"Subject\": [\"MINUTES OF A MEETING OF DIRECTORS\"], \n \"Sentiment\": [], \n \"Document Type\": [\"Memorandum\"] \n}", + "index": 0, + "logprobs": null, + "finish_reason": "stop" + } + ], + "usage": + { + "prompt_tokens": 255, + "completion_tokens": 106, + "total_tokens": 361 + } +} + diff --git a/lambda-s3/src/test/resources/chatgpt/response2.json b/lambda-s3/src/test/resources/chatgpt/response2.json new file mode 100644 index 000000000..a5def3619 --- /dev/null +++ b/lambda-s3/src/test/resources/chatgpt/response2.json @@ -0,0 +1,21 @@ +{ + "id": "cmpl-7IU7G5A8SPa6N35I1R0o5hjmVpfjM", + "object": "text_completion", + "created": 1684640026, + "model": "text-davinci-003", + "choices": + [ + { + "text": "\n\nOrganization: East Repair Inc. \nLocation: New York, NY 12240; Cambutdigo, MA 12210 \nPerson: Job Smith \nSubject: Receipt \nSentiment: None \nDocument Type: Receipt", + "index": 0, + "logprobs": null, + "finish_reason": "stop" + } + ], + "usage": + { + "prompt_tokens": 224, + "completion_tokens": 53, + "total_tokens": 277 + } +} \ No newline at end of file diff --git a/lambda-s3/src/test/resources/chatgpt/response3.json b/lambda-s3/src/test/resources/chatgpt/response3.json new file mode 100644 index 000000000..28323f2d7 --- /dev/null +++ b/lambda-s3/src/test/resources/chatgpt/response3.json @@ -0,0 +1,21 @@ +{ + "id": "cmpl-7JPQbaNp7fwGfmTv2pSs2q29O5wou", + "object": "text_completion", + "created": 1684860333, + "model": "text-davinci-003", + "choices": + [ + { + "text": "\n\n{ \n \"organization\": \"East Repair Inc.\", \n \"location\": [\"New York, NY 12240\", \"Cambutdigo, MA 12210\"], \n \"person\": \"Job Smith\", \n \"subject\": [\"Frontend eaar brake cabies\", \"New set of podal arms\", \"Labor shrs 500\"], \n \"sentiment\": null, \n \"document type\": \"Receipt\" \n}", + "index": 0, + "logprobs": null, + "finish_reason": "stop" + } + ], + "usage": + { + "prompt_tokens": 224, + "completion_tokens": 104, + "total_tokens": 328 + } +} \ No newline at end of file diff --git a/lambda-s3/src/test/resources/objectremove-event1-siteid.json b/lambda-s3/src/test/resources/objectremove-event1-siteid.json index 2c5938768..ac2b43a8a 100644 --- a/lambda-s3/src/test/resources/objectremove-event1-siteid.json +++ b/lambda-s3/src/test/resources/objectremove-event1-siteid.json @@ -20,7 +20,7 @@ "s3SchemaVersion": "1.0", "configurationId": "testConfigRule", "bucket": { - "name": "documentsbucket", + "name": "example-bucket", "ownerIdentity": { "principalId": "EXAMPLE" }, diff --git a/lambda-s3/src/test/resources/objectremove-event1.json b/lambda-s3/src/test/resources/objectremove-event1.json index 765433032..e21508f30 100644 --- a/lambda-s3/src/test/resources/objectremove-event1.json +++ b/lambda-s3/src/test/resources/objectremove-event1.json @@ -20,7 +20,7 @@ "s3SchemaVersion": "1.0", "configurationId": "testConfigRule", "bucket": { - "name": "documentsbucket", + "name": "example-bucket", "ownerIdentity": { "principalId": "EXAMPLE" }, diff --git a/lambda-s3/src/test/resources/objectremove-event2-siteid.json b/lambda-s3/src/test/resources/objectremove-event2-siteid.json index dc22df4f0..2833ae307 100644 --- a/lambda-s3/src/test/resources/objectremove-event2-siteid.json +++ b/lambda-s3/src/test/resources/objectremove-event2-siteid.json @@ -3,7 +3,7 @@ { "messageId": "7fefd1fa-51a5-4f8e-8f64-06b92fdfe2d5", "receiptHandle": "AQEByD6W3bMX3hgrwmj1rN3u31SWi+NkZU0vCfNxCtqoZDZU9JDX9DJAkeWkqIicZH+Cl5XZJ7/sRGkmPAqhdGkQo+0uiiaUrLj8D5giIxww0Io88Ht0vFJ7DoSl7EymjDtSszRxODN95VszUcBloJGpdTIzsV0diE8nl0CP6l6UW3VoJp8Bbd6UcN8WvZbAPEJiyjXuBCXcokaDnowjA/c7Vwb43f1pjp+h80LAKUDmqAEUw45ZDwkmodqOW9wZttCAa0Dz5lpiOr+gTzZSjE1BKXU5YRA1bSajVH4V6Hv+AlvzKLm4/l0a3BZk3Z9XTtKpEkddI1zovkWtYkmyPdLrSJFYz1v1GAaZOj5vgT11YxF5qPWK6aiRZ+hZP1RYb3gAMgIo7jiagzsSx1ao/aeviEbr9jeTRHLtssCEWmQn2Lw=", - "body": "{\"Records\": [{\"eventVersion\": \"2.0\",\"eventSource\": \"aws:s3\",\"awsRegion\": \"us-east-1\",\"eventTime\": \"1970-01-01T00:00:00.000Z\",\"eventName\": \"ObjectRemoved:Delete\",\"userIdentity\": {\"principalId\": \"EXAMPLE\"},\"requestParameters\": {\"sourceIPAddress\": \"127.0.0.1\"},\"responseElements\": {\"x-amz-request-id\": \"EXAMPLE123456789\",\"x-amz-id-2\": \"EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH\"},\"s3\": {\"s3SchemaVersion\": \"1.0\",\"configurationId\": \"testConfigRule\",\"bucket\": {\"name\": \"documentsbucket\",\"ownerIdentity\": {\"principalId\": \"EXAMPLE\"},\"arn\": \"arn:aws:s3:::documentsbucket\"},\"object\": {\"key\":\"test/b53c92cf-f7b9-4787-9541-76574ec70d71\",\"size\":1024,\"eTag\":\"0123456789abcdef0123456789abcdef\",\"sequencer\":\"0A1B2C3D4E5F678901\"}}}]}", + "body": "{\"Records\": [{\"eventVersion\": \"2.0\",\"eventSource\": \"aws:s3\",\"awsRegion\": \"us-east-1\",\"eventTime\": \"1970-01-01T00:00:00.000Z\",\"eventName\": \"ObjectRemoved:Delete\",\"userIdentity\": {\"principalId\": \"EXAMPLE\"},\"requestParameters\": {\"sourceIPAddress\": \"127.0.0.1\"},\"responseElements\": {\"x-amz-request-id\": \"EXAMPLE123456789\",\"x-amz-id-2\": \"EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH\"},\"s3\": {\"s3SchemaVersion\": \"1.0\",\"configurationId\": \"testConfigRule\",\"bucket\": {\"name\": \"example-bucket\",\"ownerIdentity\": {\"principalId\": \"EXAMPLE\"},\"arn\": \"arn:aws:s3:::documentsbucket\"},\"object\": {\"key\":\"test/b53c92cf-f7b9-4787-9541-76574ec70d71\",\"size\":1024,\"eTag\":\"0123456789abcdef0123456789abcdef\",\"sequencer\":\"0A1B2C3D4E5F678901\"}}}]}", "attributes": { "ApproximateReceiveCount": "1", "SentTimestamp": "1576731464019", diff --git a/lambda-s3/src/test/resources/objectremove-event2.json b/lambda-s3/src/test/resources/objectremove-event2.json index 4844d6bc6..1fe3ca2bf 100644 --- a/lambda-s3/src/test/resources/objectremove-event2.json +++ b/lambda-s3/src/test/resources/objectremove-event2.json @@ -3,7 +3,7 @@ { "messageId": "7fefd1fa-51a5-4f8e-8f64-06b92fdfe2d5", "receiptHandle": "AQEByD6W3bMX3hgrwmj1rN3u31SWi+NkZU0vCfNxCtqoZDZU9JDX9DJAkeWkqIicZH+Cl5XZJ7/sRGkmPAqhdGkQo+0uiiaUrLj8D5giIxww0Io88Ht0vFJ7DoSl7EymjDtSszRxODN95VszUcBloJGpdTIzsV0diE8nl0CP6l6UW3VoJp8Bbd6UcN8WvZbAPEJiyjXuBCXcokaDnowjA/c7Vwb43f1pjp+h80LAKUDmqAEUw45ZDwkmodqOW9wZttCAa0Dz5lpiOr+gTzZSjE1BKXU5YRA1bSajVH4V6Hv+AlvzKLm4/l0a3BZk3Z9XTtKpEkddI1zovkWtYkmyPdLrSJFYz1v1GAaZOj5vgT11YxF5qPWK6aiRZ+hZP1RYb3gAMgIo7jiagzsSx1ao/aeviEbr9jeTRHLtssCEWmQn2Lw=", - "body": "{\"Records\": [{\"eventVersion\": \"2.0\",\"eventSource\": \"aws:s3\",\"awsRegion\": \"us-east-1\",\"eventTime\": \"1970-01-01T00:00:00.000Z\",\"eventName\": \"ObjectRemoved:Delete\",\"userIdentity\": {\"principalId\": \"EXAMPLE\"},\"requestParameters\": {\"sourceIPAddress\": \"127.0.0.1\"},\"responseElements\": {\"x-amz-request-id\": \"EXAMPLE123456789\",\"x-amz-id-2\": \"EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH\"},\"s3\": {\"s3SchemaVersion\": \"1.0\",\"configurationId\": \"testConfigRule\",\"bucket\": {\"name\": \"documentsbucket\",\"ownerIdentity\": {\"principalId\": \"EXAMPLE\"},\"arn\": \"arn:aws:s3:::documentsbucket\"},\"object\": {\"key\":\"b53c92cf-f7b9-4787-9541-76574ec70d71\",\"size\":1024,\"eTag\":\"0123456789abcdef0123456789abcdef\",\"sequencer\":\"0A1B2C3D4E5F678901\"}}}]}", + "body": "{\"Records\": [{\"eventVersion\": \"2.0\",\"eventSource\": \"aws:s3\",\"awsRegion\": \"us-east-1\",\"eventTime\": \"1970-01-01T00:00:00.000Z\",\"eventName\": \"ObjectRemoved:Delete\",\"userIdentity\": {\"principalId\": \"EXAMPLE\"},\"requestParameters\": {\"sourceIPAddress\": \"127.0.0.1\"},\"responseElements\": {\"x-amz-request-id\": \"EXAMPLE123456789\",\"x-amz-id-2\": \"EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH\"},\"s3\": {\"s3SchemaVersion\": \"1.0\",\"configurationId\": \"testConfigRule\",\"bucket\": {\"name\": \"example-bucket\",\"ownerIdentity\": {\"principalId\": \"EXAMPLE\"},\"arn\": \"arn:aws:s3:::documentsbucket\"},\"object\": {\"key\":\"b53c92cf-f7b9-4787-9541-76574ec70d71\",\"size\":1024,\"eTag\":\"0123456789abcdef0123456789abcdef\",\"sequencer\":\"0A1B2C3D4E5F678901\"}}}]}", "attributes": { "ApproximateReceiveCount": "1", "SentTimestamp": "1576731464019", diff --git a/lambda-typesense/build.gradle b/lambda-typesense/build.gradle index 3c76405f0..940dd41c6 100644 --- a/lambda-typesense/build.gradle +++ b/lambda-typesense/build.gradle @@ -1,10 +1,19 @@ description = "FormKiQ Core - Lambda TypeSense" def moduleName = "formkiq-module-lambda-typesense" +def getCmd() { + String os = System.getProperty("os.name").toLowerCase() + return os.contains("win") ? "cmd" : "bash" +} + +def getCmdParam() { + String os = System.getProperty("os.name").toLowerCase() + return os.contains("win") ? "/c" : "-c" +} dependencies { - annotationProcessor group: 'com.formkiq', name: 'graalvm-annotations-processor', version: '1.2.0' + annotationProcessor group: 'com.formkiq', name: 'graalvm-annotations-processor', version: '1.3.0' implementation project(':aws-dynamodb') implementation project(':dynamodb-documents') @@ -12,12 +21,12 @@ dependencies { implementation project(':http') implementation group: 'com.amazonaws', name: 'aws-lambda-java-core', version: '1.2.2' - implementation group: 'com.google.code.gson', name: 'gson', version: '2.10' + implementation group: 'com.google.code.gson', name: 'gson', version: '2.10.1' implementation group: 'com.formkiq', name: 'lambda-runtime-graalvm', version:'2.3.1' - implementation group: 'com.formkiq', name: 'graalvm-annotations', version: '1.1.0' + implementation group: 'com.formkiq', name: 'graalvm-annotations', version: '1.2.0' - implementation group: 'org.slf4j', name: 'slf4j-simple', version: '2.0.6' + implementation group: 'org.slf4j', name: 'slf4j-simple', version: '2.0.7' testImplementation project(':fkq-test-utils') testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version:'5.9.1' @@ -43,16 +52,9 @@ nativeImage { test { failFast = true - exclude 'com/formkiq/module/lambda/typesense/awstest/**' useJUnitPlatform() } -task testaws(type: Test) { - description = 'Runs AWS integration tests.' - outputs.upToDateWhen {false} - include 'com/formkiq/module/lambda/typesense/awstest/**' -} - task buildLambdaZip(type: Zip) { dependsOn test, graalvmNativeImage inputs.files("${project.projectDir}/runtime/bootstrap", "${buildDir}/graalvm/server") @@ -91,7 +93,7 @@ task assembleTemplate { doLast { exec { - commandLine "bash", "-c", "ytt --data-value hash=${sha256} -f src/main/resources/cloudformation/typesense/template.yaml > ${buildDir}/distributions/${moduleName}/sam/typesense/template.yaml" + commandLine getCmd(), getCmdParam(), "ytt --data-value hash=${sha256} -f src/main/resources/cloudformation/typesense/template.yaml > ${buildDir}/distributions/${moduleName}/sam/typesense/template.yaml" } } } diff --git a/lambda-typesense/src/main/java/com/formkiq/module/lambda/typesense/TypesenseProcessor.java b/lambda-typesense/src/main/java/com/formkiq/module/lambda/typesense/TypesenseProcessor.java index 502030935..fde4afe9a 100644 --- a/lambda-typesense/src/main/java/com/formkiq/module/lambda/typesense/TypesenseProcessor.java +++ b/lambda-typesense/src/main/java/com/formkiq/module/lambda/typesense/TypesenseProcessor.java @@ -82,7 +82,8 @@ public class TypesenseProcessor implements RequestHandler, V */ public TypesenseProcessor() { this(System.getenv(), - new DynamoDbConnectionBuilder().setRegion(Region.of(System.getenv("AWS_REGION"))), + new DynamoDbConnectionBuilder("true".equals(System.getenv("ENABLE_AWS_X_RAY"))) + .setRegion(Region.of(System.getenv("AWS_REGION"))), EnvironmentVariableCredentialsProvider.create().resolveCredentials()); } diff --git a/lambda-typesense/src/main/resources/cloudformation/typesense/template.yaml b/lambda-typesense/src/main/resources/cloudformation/typesense/template.yaml index b4bae9827..701826949 100644 --- a/lambda-typesense/src/main/resources/cloudformation/typesense/template.yaml +++ b/lambda-typesense/src/main/resources/cloudformation/typesense/template.yaml @@ -105,7 +105,7 @@ Resources: Condition: CreateResources Properties: LogGroupName: - Fn::Sub: "/${AWS::StackName}/${TypesenseProcessor}" + Fn::Sub: "/aws/vendedlogs/${AWS::StackName}/${TypesenseProcessor}" RetentionInDays: 90 TypesenseProcessor: @@ -122,13 +122,14 @@ Resources: MemorySize: Fn::Sub: "${LambdaMemory}" CodeUri: ./formkiq-module-lambda-typesense-graalvm.zip + Tracing: Active AutoPublishCodeSha256: #@ data.values.hash or assert.fail("missing version") - ReservedConcurrentExecutions: 1 Environment: Variables: APP_ENVIRONMENT: Fn::Sub: "${AppEnvironment}" - DEBUG: false + DEBUG: false + ENABLE_AWS_X_RAY: true FormKiQType: Ref: FormKiQType DOCUMENT_SYNC_TABLE: @@ -242,6 +243,7 @@ Resources: ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole - arn:aws:iam::aws:policy/AmazonAPIGatewayInvokeFullAccess + - arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess TypesenseLambdaRolePolicy: Type: "AWS::IAM::Policy" @@ -525,10 +527,12 @@ Resources: Properties: RetentionInDays: 90 LogGroupName: - Fn::Sub: "/${AWS::StackName}/APITypeSenseAccessLogs" + Fn::Sub: "/aws/vendedlogs/${AWS::StackName}/APITypeSenseAccessLogs" Stage: Type: AWS::ApiGatewayV2::Stage + DependsOn: + - ApiGateway Condition: CreateResources Properties: ApiId: diff --git a/module-email-notify/.classpath b/module-email-notify/.classpath index 942a2d8b2..a8ba3cbd8 100644 --- a/module-email-notify/.classpath +++ b/module-email-notify/.classpath @@ -3,23 +3,35 @@ - + - + - + + + + + + + + + + + + + diff --git a/module-email-notify/build.gradle b/module-email-notify/build.gradle index 4c47c5d43..b64562641 100644 --- a/module-email-notify/build.gradle +++ b/module-email-notify/build.gradle @@ -1,38 +1,81 @@ +import org.apache.tools.ant.taskdefs.condition.Os + description = "FormKiQ Core - Email Notify Module" +sourceSets { + integration { + java.srcDir "$projectDir/src/integration/java" + resources.srcDir "$projectDir/src/integration/resources" + compileClasspath += main.output + test.output + runtimeClasspath += main.output + test.output + } +} + +configurations { + integrationImplementation.extendsFrom testImplementation + integrationRuntime.extendsFrom testRuntime +} + +def getCmd() { + String os = System.getProperty("os.name").toLowerCase() + return os.contains("win") ? "cmd" : "bash" +} + +def getCmdParam() { + String os = System.getProperty("os.name").toLowerCase() + return os.contains("win") ? "/c" : "-c" +} + jar.enabled = false dependencies { + testImplementation project(':aws-cognito') testImplementation project(':aws-s3') testImplementation project(':aws-sqs') testImplementation project(':aws-ssm') testImplementation project(':aws-sns') - testImplementation group: 'junit', name: 'junit', version:'4.+' - testImplementation group: 'com.google.code.gson', name: 'gson', version: '2.10' + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version:'5.9.1' + testImplementation group: 'com.google.code.gson', name: 'gson', version: '2.10.1' + testImplementation group: 'com.formkiq.stacks', name: 'client', version:'1.10.0' } test { - failFast = true - exclude 'com/formkiq/stacks/module/emailnotify/awstest/**' + failFast = true + useJUnitPlatform() } -task testaws(type: Test) { - description = 'Runs AWS integration tests.' - outputs.upToDateWhen {false} - include 'com/formkiq/stacks/module/emailnotify/awstest/**' +task integrationTest(type: Test) { + testClassesDirs = sourceSets.integration.output.classesDirs + classpath = sourceSets.integration.runtimeClasspath + useJUnitPlatform() } -task npmInstall(type:Exec) { +task npmInstall { dependsOn processResources, compileTestJava, checkstyleTest, spotbugsTest inputs.files("src/main/resources/js") outputs.files("${buildDir}/resources/main/js/module-email-notify.zip") - workingDir "${buildDir}/resources/main/js" - commandLine "bash", "-c", "rm *.zip ; npm install ; npm run pack" + + doLast { + + delete fileTree("${buildDir}/resources/main/js") { + include '**/*.zip' + } + exec { + workingDir "${buildDir}/resources/main/js" + commandLine getCmd(), getCmdParam(), "npm install" + } + + exec { + workingDir "${buildDir}/resources/main/js" + commandLine getCmd(), getCmdParam(), "npm run pack" + } + } } jar.dependsOn npmInstall task npmtest { + onlyIf { !Os.isFamily(Os.FAMILY_WINDOWS) } dependsOn npmInstall, jar inputs.files("src/main/resources/js") outputs.dir("${buildDir}/resources/main/js/coverage") diff --git a/module-email-notify/src/test/java/com/formkiq/stacks/module/emailnotify/awstest/EmailSendingTest.java b/module-email-notify/src/integration/java/com/formkiq/stacks/module/emailnotify/awstest/EmailSendingTest.java similarity index 89% rename from module-email-notify/src/test/java/com/formkiq/stacks/module/emailnotify/awstest/EmailSendingTest.java rename to module-email-notify/src/integration/java/com/formkiq/stacks/module/emailnotify/awstest/EmailSendingTest.java index f00b1697f..8e69da6aa 100644 --- a/module-email-notify/src/test/java/com/formkiq/stacks/module/emailnotify/awstest/EmailSendingTest.java +++ b/module-email-notify/src/integration/java/com/formkiq/stacks/module/emailnotify/awstest/EmailSendingTest.java @@ -23,17 +23,19 @@ */ package com.formkiq.stacks.module.emailnotify.awstest; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; -import org.junit.BeforeClass; -import org.junit.Ignore; -import org.junit.Test; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; import com.formkiq.aws.s3.S3ConnectionBuilder; import com.formkiq.aws.s3.S3Service; import com.formkiq.aws.sns.SnsConnectionBuilder; @@ -106,19 +108,21 @@ private static void assertSnsMessage(final String queueUrl, final String type) * * @throws IOException IOException */ - @BeforeClass + @BeforeAll public static void beforeClass() throws IOException { Region awsregion = Region.of(System.getProperty("testregion")); String awsprofile = System.getProperty("testprofile"); + final boolean enableAwsXray = false; final SqsConnectionBuilder sqsConnection = - new SqsConnectionBuilder().setCredentials(awsprofile).setRegion(awsregion); + new SqsConnectionBuilder(enableAwsXray).setCredentials(awsprofile).setRegion(awsregion); final SsmConnectionBuilder ssmBuilder = - new SsmConnectionBuilder().setCredentials(awsprofile).setRegion(awsregion); - snsBuilder = new SnsConnectionBuilder().setCredentials(awsprofile).setRegion(awsregion); + new SsmConnectionBuilder(enableAwsXray).setCredentials(awsprofile).setRegion(awsregion); + snsBuilder = + new SnsConnectionBuilder(enableAwsXray).setCredentials(awsprofile).setRegion(awsregion); final S3ConnectionBuilder s3Builder = - new S3ConnectionBuilder().setCredentials(awsprofile).setRegion(awsregion); + new S3ConnectionBuilder(enableAwsXray).setCredentials(awsprofile).setRegion(awsregion); s3Service = new S3Service(s3Builder); sqsService = new SqsService(sqsConnection); @@ -175,8 +179,9 @@ private String subscribeToSns(final String topicArn, final String queueUrl) { * * @throws Exception Exception */ - @Test(timeout = TIMEOUT) - @Ignore + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TIMEOUT) + @Disabled public void testSendingEmail01() throws Exception { // given String key = UUID.randomUUID().toString(); diff --git a/module-email-notify/src/main/resources/cloudformation/template.yaml b/module-email-notify/src/main/resources/cloudformation/template.yaml index a2cf4e9e6..2760f6720 100644 --- a/module-email-notify/src/main/resources/cloudformation/template.yaml +++ b/module-email-notify/src/main/resources/cloudformation/template.yaml @@ -81,7 +81,7 @@ Resources: Type: AWS::Logs::LogGroup Properties: LogGroupName: - Fn::Sub: "/${AWS::StackName}/${EmailNotify}" + Fn::Sub: "/aws/vendedlogs/${AWS::StackName}/${EmailNotify}" RetentionInDays: 90 EmailNotify: diff --git a/ocr/.checkstyle b/ocr/.checkstyle new file mode 100644 index 000000000..514f88730 --- /dev/null +++ b/ocr/.checkstyle @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/ocr/.classpath b/ocr/.classpath new file mode 100644 index 000000000..bfdccfd00 --- /dev/null +++ b/ocr/.classpath @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ocr/.gitignore b/ocr/.gitignore new file mode 100644 index 000000000..35fb74893 --- /dev/null +++ b/ocr/.gitignore @@ -0,0 +1,3 @@ +/bin/ +/build/ +/.gradle/ diff --git a/ocr/.project b/ocr/.project new file mode 100644 index 000000000..88892edfa --- /dev/null +++ b/ocr/.project @@ -0,0 +1,30 @@ + + + ocr + Project core created by Buildship. + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.buildship.core.gradleprojectbuilder + + + + + net.sf.eclipsecs.core.CheckstyleBuilder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.buildship.core.gradleprojectnature + net.sf.eclipsecs.core.CheckstyleNature + + + diff --git a/ocr/.settings/org.eclipse.buildship.core.prefs b/ocr/.settings/org.eclipse.buildship.core.prefs new file mode 100644 index 000000000..e47955840 --- /dev/null +++ b/ocr/.settings/org.eclipse.buildship.core.prefs @@ -0,0 +1,13 @@ +arguments= +auto.sync=false +build.scans.enabled=false +connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) +connection.project.dir= +eclipse.preferences.version=1 +gradle.user.home= +java.home= +jvm.arguments= +offline.mode=false +override.workspace.settings=false +show.console.view=false +show.executions.view=false diff --git a/ocr/.settings/org.eclipse.core.resources.prefs b/ocr/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 000000000..99f26c020 --- /dev/null +++ b/ocr/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/ocr/.settings/org.eclipse.jdt.core.prefs b/ocr/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..9ba0f3224 --- /dev/null +++ b/ocr/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,496 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.codeComplete.argumentPrefixes= +org.eclipse.jdt.core.codeComplete.argumentSuffixes= +org.eclipse.jdt.core.codeComplete.fieldPrefixes= +org.eclipse.jdt.core.codeComplete.fieldSuffixes= +org.eclipse.jdt.core.codeComplete.localPrefixes= +org.eclipse.jdt.core.codeComplete.localSuffixes= +org.eclipse.jdt.core.codeComplete.staticFieldPrefixes= +org.eclipse.jdt.core.codeComplete.staticFieldSuffixes= +org.eclipse.jdt.core.codeComplete.staticFinalFieldPrefixes= +org.eclipse.jdt.core.codeComplete.staticFinalFieldSuffixes= +org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled +org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore +org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull +org.eclipse.jdt.core.compiler.annotation.nonnull.secondary= +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary= +org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable +org.eclipse.jdt.core.compiler.annotation.nullable.secondary= +org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=11 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=11 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.doc.comment.support=enabled +org.eclipse.jdt.core.compiler.problem.APILeak=warning +org.eclipse.jdt.core.compiler.problem.annotatedTypeArgumentToUnannotated=info +org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.autoboxing=warning +org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning +org.eclipse.jdt.core.compiler.problem.deadCode=warning +org.eclipse.jdt.core.compiler.problem.deprecation=warning +org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled +org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled +org.eclipse.jdt.core.compiler.problem.discouragedReference=warning +org.eclipse.jdt.core.compiler.problem.emptyStatement=warning +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore +org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=warning +org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning +org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning +org.eclipse.jdt.core.compiler.problem.forbiddenReference=error +org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning +org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled +org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning +org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning +org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=warning +org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning +org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=disabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=disabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=public +org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled +org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning +org.eclipse.jdt.core.compiler.problem.missingJavadocComments=warning +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public +org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag +org.eclipse.jdt.core.compiler.problem.missingJavadocTags=warning +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=public +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning +org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore +org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning +org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning +org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore +org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning +org.eclipse.jdt.core.compiler.problem.nonnullTypeVariableFromLegacyInvocation=warning +org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error +org.eclipse.jdt.core.compiler.problem.nullReference=warning +org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error +org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=warning +org.eclipse.jdt.core.compiler.problem.pessimisticNullAnalysisForFreeTypeVariables=warning +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning +org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning +org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=warning +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning +org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning +org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.suppressWarningsNotFullyAnalysed=info +org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=warning +org.eclipse.jdt.core.compiler.problem.terminalDeprecation=warning +org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning +org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled +org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning +org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning +org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore +org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning +org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentType=warning +org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentTypeStrict=disabled +org.eclipse.jdt.core.compiler.problem.unlikelyEqualsArgumentType=info +org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=warning +org.eclipse.jdt.core.compiler.problem.unstableAutoModuleName=warning +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled +org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedImport=warning +org.eclipse.jdt.core.compiler.problem.unusedLabel=warning +org.eclipse.jdt.core.compiler.problem.unusedLocal=warning +org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=warning +org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled +org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning +org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.source=11 +org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns=false +org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647 +org.eclipse.jdt.core.formatter.align_type_members_on_columns=false +org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns=false +org.eclipse.jdt.core.formatter.align_with_spaces=false +org.eclipse.jdt.core.formatter.alignment_for_additive_operator=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_assignment=16 +org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_loops=16 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression_chain=0 +org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header=0 +org.eclipse.jdt.core.formatter.alignment_for_logical_operator=16 +org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_module_statements=16 +org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +org.eclipse.jdt.core.formatter.alignment_for_multiplicative_operator=16 +org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references=0 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_relational_operator=0 +org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80 +org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_shift_operator=0 +org.eclipse.jdt.core.formatter.alignment_for_string_concatenation=16 +org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_type_arguments=0 +org.eclipse.jdt.core.formatter.alignment_for_type_parameters=0 +org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16 +org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_after_package=1 +org.eclipse.jdt.core.formatter.blank_lines_before_field=0 +org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 +org.eclipse.jdt.core.formatter.blank_lines_before_imports=0 +org.eclipse.jdt.core.formatter.blank_lines_before_member_type=0 +org.eclipse.jdt.core.formatter.blank_lines_before_method=1 +org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 +org.eclipse.jdt.core.formatter.blank_lines_before_package=0 +org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=0 +org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=2 +org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.comment.align_tags_descriptions_grouped=false +org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions=false +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false +org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position=true +org.eclipse.jdt.core.formatter.comment.format_block_comments=true +org.eclipse.jdt.core.formatter.comment.format_header=true +org.eclipse.jdt.core.formatter.comment.format_html=true +org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true +org.eclipse.jdt.core.formatter.comment.format_line_comments=true +org.eclipse.jdt.core.formatter.comment.format_source_code=true +org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false +org.eclipse.jdt.core.formatter.comment.indent_root_tags=true +org.eclipse.jdt.core.formatter.comment.indent_tag_description=false +org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert +org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert +org.eclipse.jdt.core.formatter.comment.line_length=100 +org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true +org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true +org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false +org.eclipse.jdt.core.formatter.compact_else_if=true +org.eclipse.jdt.core.formatter.continuation_indentation=2 +org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2 +org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off +org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on +org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false +org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true +org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_empty_lines=false +org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true +org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true +org.eclipse.jdt.core.formatter.indentation.size=2 +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert +org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_after_additive_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_bitwise_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert +org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_after_logical_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_multiplicative_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_relational_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert +org.eclipse.jdt.core.formatter.insert_space_after_shift_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_string_concatenation=insert +org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_additive_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_bitwise_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_before_logical_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_multiplicative_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert +org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_relational_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_shift_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation=insert +org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.join_lines_in_comments=true +org.eclipse.jdt.core.formatter.join_wrapped_lines=true +org.eclipse.jdt.core.formatter.keep_annotation_declaration_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_anonymous_type_declaration_on_one_line=one_line_if_empty +org.eclipse.jdt.core.formatter.keep_code_block_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false +org.eclipse.jdt.core.formatter.keep_enum_constant_declaration_on_one_line=one_line_if_empty +org.eclipse.jdt.core.formatter.keep_enum_declaration_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_if_then_body_block_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false +org.eclipse.jdt.core.formatter.keep_lambda_body_block_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_loop_body_block_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_method_body_on_one_line=one_line_if_empty +org.eclipse.jdt.core.formatter.keep_simple_do_while_body_on_same_line=false +org.eclipse.jdt.core.formatter.keep_simple_for_body_on_same_line=false +org.eclipse.jdt.core.formatter.keep_simple_getter_setter_on_one_line=false +org.eclipse.jdt.core.formatter.keep_simple_while_body_on_same_line=false +org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_type_declaration_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.lineSplit=100 +org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false +org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 +org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=3 +org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause=common_lines +org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=false +org.eclipse.jdt.core.formatter.tabulation.char=space +org.eclipse.jdt.core.formatter.tabulation.size=2 +org.eclipse.jdt.core.formatter.use_on_off_tags=true +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false +org.eclipse.jdt.core.formatter.wrap_before_additive_operator=true +org.eclipse.jdt.core.formatter.wrap_before_assignment_operator=false +org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true +org.eclipse.jdt.core.formatter.wrap_before_bitwise_operator=true +org.eclipse.jdt.core.formatter.wrap_before_conditional_operator=true +org.eclipse.jdt.core.formatter.wrap_before_logical_operator=true +org.eclipse.jdt.core.formatter.wrap_before_multiplicative_operator=true +org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true +org.eclipse.jdt.core.formatter.wrap_before_relational_operator=true +org.eclipse.jdt.core.formatter.wrap_before_shift_operator=true +org.eclipse.jdt.core.formatter.wrap_before_string_concatenation=true +org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true +org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter diff --git a/ocr/.settings/org.eclipse.jdt.ui.prefs b/ocr/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 000000000..c13d929fd --- /dev/null +++ b/ocr/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,8 @@ +eclipse.preferences.version=1 +formatter_profile=_GoogleStyle +formatter_settings_version=16 +org.eclipse.jdt.ui.exception.name=e +org.eclipse.jdt.ui.gettersetter.use.is=true +org.eclipse.jdt.ui.keywordthis=true +org.eclipse.jdt.ui.overrideannotation=true +org.eclipse.jdt.ui.text.custom_code_templates= diff --git a/ocr/build.gradle b/ocr/build.gradle new file mode 100644 index 000000000..c34e40178 --- /dev/null +++ b/ocr/build.gradle @@ -0,0 +1,22 @@ + +description = "Ocr" + +dependencies { + + annotationProcessor group: 'com.formkiq', name: 'graalvm-annotations-processor', version: '1.4.0' + + implementation group: 'com.formkiq', name: 'graalvm-annotations', version: '1.2.0' + + implementation project(':actions') + implementation project(':aws-dynamodb') + implementation project(':dynamodb-documents') + implementation project(':aws-s3') + implementation project(':aws-sqs') + implementation project(':fkq-lambda-services') + implementation project(':fkq-lambda-core') + implementation project(':fkq-plugins') + implementation group: 'com.amazonaws', name: 'aws-lambda-java-core', version: '1.2.2' + implementation group: 'com.google.code.gson', name: 'gson', version: '2.10.1' + + implementation group: 'com.itextpdf', name: 'kernel', version: '7.2.5' +} \ No newline at end of file diff --git a/ocr/config/checkstyle/checkstyle.xml b/ocr/config/checkstyle/checkstyle.xml new file mode 100644 index 000000000..9077493f9 --- /dev/null +++ b/ocr/config/checkstyle/checkstyle.xml @@ -0,0 +1,239 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ocr/config/checkstyle/import-control.xml b/ocr/config/checkstyle/import-control.xml new file mode 100644 index 000000000..85cc85d54 --- /dev/null +++ b/ocr/config/checkstyle/import-control.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ocr/config/checkstyle/mysuppressions.xml b/ocr/config/checkstyle/mysuppressions.xml new file mode 100644 index 000000000..8a00de3e5 --- /dev/null +++ b/ocr/config/checkstyle/mysuppressions.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/ocr/src/main/java/com/formkiq/module/ocr/DocumentOcrService.java b/ocr/src/main/java/com/formkiq/module/ocr/DocumentOcrService.java new file mode 100644 index 000000000..857956d12 --- /dev/null +++ b/ocr/src/main/java/com/formkiq/module/ocr/DocumentOcrService.java @@ -0,0 +1,138 @@ +/** + * MIT License + * + * Copyright (c) 2018 - 2020 FormKiQ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.formkiq.module.ocr; + +import java.util.List; +import com.amazonaws.services.lambda.runtime.LambdaLogger; +import com.formkiq.aws.dynamodb.DynamicObject; +import com.formkiq.module.lambdaservices.AwsServiceCache; + +/** + * + * Document OCR Service. + * + */ +public interface DocumentOcrService { + + /** Prefix Temp File. */ + String PREFIX_TEMP_FILES = "tempfiles/"; + + /** + * Optical character recognition of Document. + * + * @param logger {@link LambdaLogger} + * @param awsservice {@link AwsServiceCache} + * @param request {@link OcrRequest} + * @param siteId {@link String} + * @param documentId {@link String} + * @param userId {@link String} + */ + void convert(LambdaLogger logger, AwsServiceCache awsservice, OcrRequest request, String siteId, + String documentId, String userId); + + /** + * Delete Document OCR. + * + * @param siteId {@link String} + * @param documentId {@link String} + */ + void delete(String siteId, String documentId); + + /** + * Get Document OCR. + * + * @param siteId {@link String} + * @param documentId {@link String} + * @return {@link DynamicObject} + */ + DynamicObject get(String siteId, String documentId); + + /** + * Get / Find OCR Document S3 Key. + * + * @param siteId {@link String} + * @param documentId {@link String} + * @param jobId {@link String} + * @return {@link List} {@link String} + */ + List getOcrS3Keys(String siteId, String documentId, String jobId); + + /** + * Get S3 Key for OCR document. + * + * @param siteId {@link String} + * @param documentId {@link String} + * @param jobId {@link String} + * @return {@link String} + */ + String getS3Key(String siteId, String documentId, String jobId); + + /** + * Save {@link Ocr}. + * + * @param ocr {@link Ocr} + */ + void save(Ocr ocr); + + /** + * Set Optical character recognition of Document. + * + * @param awsservice {@link AwsServiceCache} + * @param siteId {@link String} + * @param documentId {@link String} + * @param userId {@link String} + * @param content {@link String} + * @param contentType {@link String} + */ + void set(AwsServiceCache awsservice, String siteId, String documentId, String userId, + String content, String contentType); + + /** + * Converts OCR to Raw text. + * + * @param content {@link String} + * @return {@link String} + */ + String toText(String content); + + /** + * Update OCR Scan Status and call next action. + * + * @param awsservice {@link AwsServiceCache} + * @param siteId {@link String} + * @param documentId {@link String} + * @param status {@link OcrScanStatus} + */ + void updateOcrScanStatus(AwsServiceCache awsservice, String siteId, String documentId, + OcrScanStatus status); + + /** + * Update OCR Scan Status. + * + * @param siteId {@link String} + * @param documentId {@link String} + * @param status {@link OcrScanStatus} + */ + void updateOcrScanStatus(String siteId, String documentId, OcrScanStatus status); +} diff --git a/ocr/src/main/java/com/formkiq/module/ocr/DocumentOcrServiceExtension.java b/ocr/src/main/java/com/formkiq/module/ocr/DocumentOcrServiceExtension.java new file mode 100644 index 000000000..93d9d1f9b --- /dev/null +++ b/ocr/src/main/java/com/formkiq/module/ocr/DocumentOcrServiceExtension.java @@ -0,0 +1,62 @@ +/** + * MIT License + * + * Copyright (c) 2018 - 2020 FormKiQ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.formkiq.module.ocr; + +import com.formkiq.aws.dynamodb.DynamoDbConnectionBuilder; +import com.formkiq.aws.s3.S3Service; +import com.formkiq.module.lambdaservices.AwsServiceCache; +import com.formkiq.module.lambdaservices.AwsServiceExtension; + +/** + * + * {@link DocumentOcrServiceExtension} for {@link DocumentOcrService}. + * + */ +public class DocumentOcrServiceExtension implements AwsServiceExtension { + + /** {@link DocumentOcrService}. */ + private DocumentOcrService service = null; + + /** + * constructor. + * + */ + public DocumentOcrServiceExtension() {} + + @Override + public DocumentOcrService loadService(final AwsServiceCache awsServiceCache) { + + if (this.service == null) { + DynamoDbConnectionBuilder db = awsServiceCache.getExtension(DynamoDbConnectionBuilder.class); + String documentsTable = awsServiceCache.environment("DOCUMENTS_TABLE"); + S3Service s3 = awsServiceCache.getExtension(S3Service.class); + String ocrS3Bucket = awsServiceCache.environment("OCR_S3_BUCKET"); + String documentsS3Bucket = awsServiceCache.environment("DOCUMENTS_S3_BUCKET"); + this.service = + new DocumentOcrServiceTesseract(db, documentsTable, s3, ocrS3Bucket, documentsS3Bucket); + } + + return this.service; + } +} diff --git a/ocr/src/main/java/com/formkiq/module/ocr/DocumentOcrServiceTesseract.java b/ocr/src/main/java/com/formkiq/module/ocr/DocumentOcrServiceTesseract.java new file mode 100644 index 000000000..5f8805fd5 --- /dev/null +++ b/ocr/src/main/java/com/formkiq/module/ocr/DocumentOcrServiceTesseract.java @@ -0,0 +1,410 @@ +/** + * MIT License + * + * Copyright (c) 2018 - 2020 FormKiQ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.formkiq.module.ocr; + +import static com.formkiq.aws.dynamodb.SiteIdKeyGenerator.createS3Key; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.stream.Collectors; +import com.amazonaws.services.lambda.runtime.LambdaLogger; +import com.formkiq.aws.dynamodb.AttributeValueToDynamicObject; +import com.formkiq.aws.dynamodb.DbKeys; +import com.formkiq.aws.dynamodb.DynamicObject; +import com.formkiq.aws.dynamodb.DynamoDbConnectionBuilder; +import com.formkiq.aws.dynamodb.DynamoDbService; +import com.formkiq.aws.dynamodb.DynamoDbServiceImpl; +import com.formkiq.aws.dynamodb.SiteIdKeyGenerator; +import com.formkiq.aws.dynamodb.objects.DateUtil; +import com.formkiq.aws.dynamodb.objects.MimeType; +import com.formkiq.aws.s3.S3ObjectMetadata; +import com.formkiq.aws.s3.S3Service; +import com.formkiq.aws.sqs.SqsService; +import com.formkiq.module.actions.Action; +import com.formkiq.module.actions.ActionStatus; +import com.formkiq.module.actions.ActionType; +import com.formkiq.module.actions.services.ActionsNotificationService; +import com.formkiq.module.actions.services.ActionsService; +import com.formkiq.module.lambdaservices.AwsServiceCache; +import com.formkiq.module.ocr.pdf.PdfPortfolio; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.itextpdf.kernel.pdf.PdfDocument; +import com.itextpdf.kernel.pdf.PdfReader; +import software.amazon.awssdk.services.dynamodb.model.AttributeValue; +import software.amazon.awssdk.services.s3.model.ListObjectsResponse; +import software.amazon.awssdk.services.s3.model.S3Object; + +/** + * + * DynamoDb implementation of {@link DocumentOcrService}. + * + */ +public class DocumentOcrServiceTesseract implements DocumentOcrService, DbKeys { + + /** Content Type APPLICATION/JSON. */ + private static final String APPLICATION_JSON = "application/json"; + /** Document OCR Prefix. */ + private static final String PREFIX_DOCUMENT_OCR = "ocr" + DbKeys.TAG_DELIMINATOR; + + /** {@link DynamoDbService}. */ + private DynamoDbService db; + /** {@link SimpleDateFormat} in ISO Standard format. */ + private SimpleDateFormat df = DateUtil.getIsoDateFormatter(); + /** {@link String}. */ + private String documentsBucket; + /** {@link Gson}. */ + private Gson gson = new GsonBuilder().disableHtmlEscaping().create(); + /** OCR S3 Bucket. */ + private String ocrBucket; + /** {@link S3Service}. */ + private S3Service s3; + + + /** + * constructor. + * + * @param connection {@link DynamoDbConnectionBuilder} + * @param ocrTable {@link String} + * @param s3Service {@link S3Service} + * @param ocrS3Bucket {@link String} + * @param documentsS3Bucket {@link String} + */ + public DocumentOcrServiceTesseract(final DynamoDbConnectionBuilder connection, + final String ocrTable, final S3Service s3Service, final String ocrS3Bucket, + final String documentsS3Bucket) { + + if (ocrTable == null) { + throw new IllegalArgumentException("'ocrTable' is null"); + } + + if (ocrS3Bucket == null) { + throw new IllegalArgumentException("'ocrS3Bucket' is null"); + } + + if (documentsS3Bucket == null) { + throw new IllegalArgumentException("'documentsS3Bucket' is null"); + } + + this.db = new DynamoDbServiceImpl(connection, ocrTable); + this.s3 = s3Service; + this.ocrBucket = ocrS3Bucket; + this.documentsBucket = documentsS3Bucket; + } + + @Override + public void convert(final LambdaLogger logger, final AwsServiceCache awsservice, + final OcrRequest request, final String siteId, final String documentId, final String userId) { + + String s3key = createS3Key(siteId, documentId); + + String contentType = getS3FileContentType(this.documentsBucket, s3key); + + if (MimeType.isPlainText(contentType)) { + + if (awsservice.debug()) { + String msg = String.format("saving text document %s in bucket %s by user %s", s3key, + this.documentsBucket, userId); + logger.log(msg); + } + + updatePlainText(awsservice, siteId, documentId, userId, s3key, contentType); + + } else { + + String documentS3toConvert = updateS3ObjectIfNecessary(s3key, contentType); + + if (awsservice.debug()) { + String msg = String.format("converting document %s in bucket by user %s", s3key, + this.documentsBucket, userId); + logger.log(msg); + } + + String jobId = convertDocument(awsservice, request, siteId, documentId, s3key, + documentS3toConvert, contentType); + + Ocr ocr = new Ocr().siteId(siteId).documentId(documentId).jobId(jobId).engine(getOcrEngine()) + .status(OcrScanStatus.REQUESTED).contentType(APPLICATION_JSON).userId(userId) + .addPdfDetectedCharactersAsText(request.isAddPdfDetectedCharactersAsText()); + + save(ocr); + } + } + + /** + * OCR Convert Document. + * + * @param awsservice {@link AwsServiceCache} + * @param request {@link OcrRequest} + * @param siteId {@link String} + * @param documentId {@link String} + * @param s3key original s3 key + * @param documentS3toConvert Document S3 Key to convert. + * @param contentType {@link String} + * @return {@link String} + */ + protected String convertDocument(final AwsServiceCache awsservice, final OcrRequest request, + final String siteId, final String documentId, final String s3key, + final String documentS3toConvert, final String contentType) { + + String jobId = UUID.randomUUID().toString(); + + OcrSqsMessage msg = new OcrSqsMessage().jobId(jobId).siteId(siteId).documentId(documentId) + .contentType(contentType); + + String json = this.gson.toJson(msg); + + String sqsQueueUrl = awsservice.environment("OCR_SQS_QUEUE_URL"); + SqsService sqsService = awsservice.getExtension(SqsService.class); + sqsService.sendMessage(sqsQueueUrl, json); + + return jobId; + } + + @Override + public void delete(final String siteId, final String documentId) { + + String prefix = getS3Key(siteId, documentId, null); + + ListObjectsResponse response = this.s3.listObjects(this.ocrBucket, prefix); + response.contents().forEach(resp -> { + this.s3.deleteObject(this.ocrBucket, resp.key(), null); + }); + + Map map = keysDocumentOcr(siteId, documentId); + this.db.deleteItem(map.get(PK), map.get(SK)); + } + + @Override + public DynamicObject get(final String siteId, final String documentId) { + + DynamicObject obj = null; + + Map keys = keysDocumentOcr(siteId, documentId); + Map result = this.db.get(keys.get(PK), keys.get(SK)); + + if (!result.isEmpty()) { + AttributeValueToDynamicObject transform = new AttributeValueToDynamicObject(); + obj = transform.apply(result); + } + + return obj; + } + + protected OcrEngine getOcrEngine() { + return OcrEngine.TESSERACT; + } + + @Override + public List getOcrS3Keys(final String siteId, final String documentId, + final String jobId) { + + String prefix = getS3Key(siteId, documentId, jobId); + + ListObjectsResponse list = this.s3.listObjects(this.ocrBucket, prefix); + + List s3Keys = + list.contents().stream().filter(f -> !f.key().contains(".s3_access_check")) + .map(f -> f.key()).sorted(new S3KeysNaturalComparator()).collect(Collectors.toList()); + + return s3Keys; + } + + /** + * Get S3 File Content-Type. + * + * @param bucket {@link String} + * @param key {@link String} + * @return {@link String} + */ + private String getS3FileContentType(final String bucket, final String key) { + + S3ObjectMetadata meta = this.s3.getObjectMetadata(bucket, key, null); + return meta.getContentType(); + } + + @Override + public String getS3Key(final String siteId, final String documentId, final String jobId) { + String s3key = SiteIdKeyGenerator.createS3Key(siteId, documentId); + return s3key; + } + + /** + * Document Formats Key {@link AttributeValue}. + * + * @param siteId {@link String} + * @param documentId {@link String} + * @return {@link Map} + */ + private Map keysDocumentOcr(final String siteId, + final String documentId) { + return keysGeneric(siteId, PREFIX_DOCS + documentId, PREFIX_DOCUMENT_OCR); + } + + @Override + public void save(final Ocr ocr) { + String fulldate = this.df.format(new Date()); + + Map pkvalues = keysDocumentOcr(ocr.siteId(), ocr.documentId()); + + addS(pkvalues, "documentId", ocr.documentId()); + addS(pkvalues, "insertedDate", fulldate); + addS(pkvalues, "contentType", ocr.contentType()); + addS(pkvalues, "userId", ocr.userId()); + addS(pkvalues, "jobId", ocr.jobId()); + addS(pkvalues, "ocrEngine", ocr.engine().name().toLowerCase()); + addS(pkvalues, "ocrStatus", ocr.status().name().toLowerCase()); + addS(pkvalues, "addPdfDetectedCharactersAsText", + ocr.addPdfDetectedCharactersAsText() ? "true" : "false"); + + this.db.putItem(pkvalues); + } + + @Override + public void set(final AwsServiceCache awsservice, final String siteId, final String documentId, + final String userId, final String content, final String contentType) { + + this.delete(siteId, documentId); + + String jobId = UUID.randomUUID().toString(); + String s3Key = getS3Key(siteId, documentId, jobId); + + this.s3.putObject(this.ocrBucket, s3Key, content.getBytes(StandardCharsets.UTF_8), contentType); + + Ocr ocr = new Ocr().siteId(siteId).documentId(documentId).jobId(jobId).engine(OcrEngine.MANUAL) + .status(OcrScanStatus.SUCCESSFUL).contentType(contentType).userId(userId); + + save(ocr); + + updateOcrScanStatus(awsservice, siteId, documentId, OcrScanStatus.SUCCESSFUL); + } + + @Override + public String toText(final String content) { + return content; + } + + @Override + public void updateOcrScanStatus(final AwsServiceCache awsservice, final String siteId, + final String documentId, final OcrScanStatus status) { + + updateOcrScanStatus(siteId, documentId, status); + + ActionStatus actionStatus = OcrScanStatus.FAILED.equals(status) ? ActionStatus.FAILED + : OcrScanStatus.SKIPPED.equals(status) ? ActionStatus.SKIPPED : ActionStatus.COMPLETE; + + ActionsService actions = awsservice.getExtension(ActionsService.class); + + List actionlist = + actions.updateActionStatus(siteId, documentId, ActionType.OCR, actionStatus); + + ActionsNotificationService notificationService = + awsservice.getExtension(ActionsNotificationService.class); + notificationService.publishNextActionEvent(actionlist, siteId, documentId); + } + + @Override + public void updateOcrScanStatus(final String siteId, final String documentId, + final OcrScanStatus status) { + + Map pkvalues = keysDocumentOcr(siteId, documentId); + Map attributeValues = + Map.of("ocrStatus", AttributeValue.builder().s(status.name().toLowerCase()).build()); + + this.db.updateFields(pkvalues.get(PK), pkvalues.get(SK), attributeValues); + } + + /** + * Update Plain Text OCR. + * + * @param awsservice {@link AwsServiceCache} + * @param siteId {@link String} + * @param documentId {@link String} + * @param userId {@link String} + * @param s3Key {@link S3Object} + * @param contentType {@link String} + */ + private void updatePlainText(final AwsServiceCache awsservice, final String siteId, + final String documentId, final String userId, final String s3Key, final String contentType) { + + String jobId = UUID.randomUUID().toString(); + + String content = this.s3.getContentAsString(this.documentsBucket, s3Key, null); + + String ocrS3Key = getS3Key(siteId, documentId, jobId); + this.s3.putObject(this.ocrBucket, ocrS3Key, content.getBytes(StandardCharsets.UTF_8), + contentType); + + OcrScanStatus status = OcrScanStatus.SKIPPED; + + Ocr ocr = new Ocr().siteId(siteId).documentId(documentId).jobId(jobId).engine(getOcrEngine()) + .status(status).contentType(contentType).userId(userId); + + save(ocr); + + updateOcrScanStatus(awsservice, siteId, documentId, status); + } + + /** + * Is {@link S3Object} a PDF, if so check if it's a PDF Portfolio. + * + * @param s3Key {@link String} + * @param contentType {@link String} + * @return {@link String} + */ + private String updateS3ObjectIfNecessary(final String s3Key, final String contentType) { + + String key = s3Key; + + if (contentType.contains("application/pdf")) { + + try (InputStream is = this.s3.getContentAsInputStream(this.documentsBucket, key)) { + + try (PdfDocument doc = new PdfDocument(new PdfReader(is))) { + + if (PdfPortfolio.isPdfPortfolio(doc)) { + + ByteArrayOutputStream os = new ByteArrayOutputStream(); + PdfPortfolio.mergePdfPortfolios(doc, os); + + key = PREFIX_TEMP_FILES + key; + this.s3.putObject(this.ocrBucket, key, os.toByteArray(), "application/pdf"); + } + } + + } catch (IOException e) { + key = s3Key; + } + } + + return key; + } + +} diff --git a/ocr/src/main/java/com/formkiq/module/ocr/DocumentsOcrRequestHandler.java b/ocr/src/main/java/com/formkiq/module/ocr/DocumentsOcrRequestHandler.java new file mode 100644 index 000000000..0c396d4be --- /dev/null +++ b/ocr/src/main/java/com/formkiq/module/ocr/DocumentsOcrRequestHandler.java @@ -0,0 +1,281 @@ +/** + * MIT License + * + * Copyright (c) 2018 - 2020 FormKiQ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.formkiq.module.ocr; + +import static com.formkiq.aws.services.lambda.ApiGatewayRequestEventUtil.getCallingCognitoUsername; +import static com.formkiq.aws.services.lambda.ApiResponseStatus.SC_NOT_FOUND; +import static com.formkiq.aws.services.lambda.ApiResponseStatus.SC_OK; +import static com.formkiq.module.ocr.DocumentOcrService.PREFIX_TEMP_FILES; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import com.amazonaws.services.lambda.runtime.LambdaLogger; +import com.formkiq.aws.dynamodb.DynamicObject; +import com.formkiq.aws.s3.PresignGetUrlConfig; +import com.formkiq.aws.s3.S3Service; +import com.formkiq.aws.services.lambda.ApiAuthorizer; +import com.formkiq.aws.services.lambda.ApiGatewayRequestEvent; +import com.formkiq.aws.services.lambda.ApiGatewayRequestEventUtil; +import com.formkiq.aws.services.lambda.ApiGatewayRequestHandler; +import com.formkiq.aws.services.lambda.ApiMapResponse; +import com.formkiq.aws.services.lambda.ApiRequestHandlerResponse; +import com.formkiq.aws.services.lambda.ApiResponseStatus; +import com.formkiq.aws.services.lambda.exceptions.BadException; +import com.formkiq.aws.services.lambda.exceptions.NotFoundException; +import com.formkiq.module.lambdaservices.AwsServiceCache; +import com.formkiq.stacks.dynamodb.DocumentService; + +/** {@link ApiGatewayRequestHandler} for "/documents/{documentId}/ocr". */ +public class DocumentsOcrRequestHandler + implements ApiGatewayRequestHandler, ApiGatewayRequestEventUtil { + + /** {@link DocumentsOcrRequestHandler} URL. */ + public static final String URL = "/documents/{documentId}/ocr"; + + /** + * constructor. + * + */ + public DocumentsOcrRequestHandler() {} + + /** + * Build Get Response. + * + * @param obj {@link DynamicObject} + * @param documentId {@link String} + * @return {@link Map} + */ + private Map buildGetResponse(final DynamicObject obj, final String documentId) { + Map map = new HashMap<>(); + map.put("documentId", documentId); + + if (obj != null) { + + map.put("insertedDate", obj.get("insertedDate")); + map.put("contentType", obj.get("contentType")); + map.put("userId", obj.get("userId")); + map.put("ocrEngine", obj.get("ocrEngine")); + map.put("ocrStatus", obj.get("ocrStatus")); + } + + return map; + } + + @Override + public ApiRequestHandlerResponse delete(final LambdaLogger logger, + final ApiGatewayRequestEvent event, final ApiAuthorizer authorizer, + final AwsServiceCache awsservice) throws Exception { + + ApiMapResponse resp = new ApiMapResponse(); + String siteId = authorizer.getSiteId(); + String documentId = event.getPathParameters().get("documentId"); + + DocumentOcrService ocrService = awsservice.getExtension(DocumentOcrService.class); + ocrService.delete(siteId, documentId); + + return new ApiRequestHandlerResponse(SC_OK, resp); + } + + @Override + public ApiRequestHandlerResponse get(final LambdaLogger logger, + final ApiGatewayRequestEvent event, final ApiAuthorizer authorizer, + final AwsServiceCache awsservice) throws Exception { + + ApiResponseStatus status = SC_OK; + String siteId = authorizer.getSiteId(); + String documentId = event.getPathParameters().get("documentId"); + + final boolean contentUrl = event.getQueryStringParameters() != null + && event.getQueryStringParameters().containsKey("contentUrl"); + + final boolean textOnly = event.getQueryStringParameters() != null + && event.getQueryStringParameters().containsKey("text"); + + DocumentOcrService ocrService = awsservice.getExtension(DocumentOcrService.class); + + DynamicObject obj = ocrService.get(siteId, documentId); + + Map map = buildGetResponse(obj, documentId); + + if (map.containsKey("ocrStatus")) { + + if (map.get("ocrStatus").equals(OcrScanStatus.SUCCESSFUL.name().toLowerCase())) { + + S3Service s3 = awsservice.getExtension(S3Service.class); + + String jobId = obj.getString("jobId"); + + List s3Keys = ocrService.getOcrS3Keys(siteId, documentId, jobId); + if (s3Keys.isEmpty()) { + throw new NotFoundException("OCR results not found"); + } + + if (contentUrl) { + + List contentUrls = getContentUrls(awsservice, ocrService, s3, s3Keys, textOnly); + map.put("contentUrls", contentUrls); + + } else { + + String content = getS3Content(awsservice, ocrService, s3, s3Keys, textOnly); + map.put("data", content); + } + + } + + } else { + status = SC_NOT_FOUND; + } + + ApiMapResponse resp = new ApiMapResponse(map); + return new ApiRequestHandlerResponse(status, resp); + } + + /** + * Get S3 Content Urls. + * + * @param awsservice {@link AwsServiceCache} + * @param ocrService {@link DocumentOcrService} + * @param s3 {@link S3Service} + * @param s3Keys {@link List} {@link String} + * @param textOnly boolean + * @return {@link List} {@link String} + */ + private List getContentUrls(final AwsServiceCache awsservice, + final DocumentOcrService ocrService, final S3Service s3, final List s3Keys, + final boolean textOnly) { + + String ocrBucket = awsservice.environment("OCR_S3_BUCKET"); + List newS3Keys = new ArrayList<>(); + + if (textOnly) { + + s3Keys.forEach(s3Key -> { + String content = s3.getContentAsString(ocrBucket, s3Key, null); + content = ocrService.toText(content); + + String newKey = PREFIX_TEMP_FILES + s3Key; + s3.putObject(ocrBucket, newKey, content.getBytes(StandardCharsets.UTF_8), "text/plain"); + + newS3Keys.add(newKey); + }); + + } else { + newS3Keys.addAll(s3Keys); + } + + PresignGetUrlConfig config = new PresignGetUrlConfig(); + List contentUrls = newS3Keys.stream().map( + s3key -> s3.presignGetUrl(ocrBucket, s3key, Duration.ofHours(1), null, config).toString()) + .collect(Collectors.toList()); + return contentUrls; + } + + @Override + public String getRequestUrl() { + return URL; + } + + /** + * Get S3 Content. + * + * @param awsservice {@link AwsServiceCache} + * @param ocrService {@link DocumentOcrService} + * @param s3 {@link S3Service} + * @param s3Keys {@link List} {@link String} + * @param textOnly boolean + * @return {@link String} + */ + private String getS3Content(final AwsServiceCache awsservice, final DocumentOcrService ocrService, + final S3Service s3, final List s3Keys, final boolean textOnly) { + + String ocrBucket = awsservice.environment("OCR_S3_BUCKET"); + StringBuilder sb = new StringBuilder(); + + for (String s3Key : s3Keys) { + String content = s3.getContentAsString(ocrBucket, s3Key, null); + + if (textOnly) { + content = ocrService.toText(content); + } + + sb.append(content); + } + + return sb.toString(); + } + + @Override + public ApiRequestHandlerResponse post(final LambdaLogger logger, + final ApiGatewayRequestEvent event, final ApiAuthorizer authorizer, + final AwsServiceCache awsservice) throws Exception { + + ApiMapResponse resp = new ApiMapResponse(); + String siteId = authorizer.getSiteId(); + String documentId = event.getPathParameters().get("documentId"); + + DocumentService ds = awsservice.getExtension(DocumentService.class); + if (!ds.exists(siteId, documentId)) { + throw new NotFoundException("Document " + documentId + " not found."); + } + + OcrRequest request = fromBodyToObject(logger, event, OcrRequest.class); + String userId = getCallingCognitoUsername(event); + + DocumentOcrService ocrService = awsservice.getExtension(DocumentOcrService.class); + ocrService.convert(logger, awsservice, request, siteId, documentId, userId); + + return new ApiRequestHandlerResponse(SC_OK, resp); + } + + + @Override + public ApiRequestHandlerResponse put(final LambdaLogger logger, + final ApiGatewayRequestEvent event, final ApiAuthorizer authorizer, + final AwsServiceCache awsservice) throws Exception { + + ApiMapResponse resp = new ApiMapResponse(); + String siteId = authorizer.getSiteId(); + String documentId = event.getPathParameters().get("documentId"); + + String userId = getCallingCognitoUsername(event); + + Map map = fromBodyToMap(logger, event); + String contentType = (String) map.get("contentType"); + String content = (String) map.get("content"); + + if (contentType == null || content == null) { + throw new BadException("'content' and 'contentType' are required"); + } + + DocumentOcrService ocrService = awsservice.getExtension(DocumentOcrService.class); + ocrService.set(awsservice, siteId, documentId, userId, content, contentType); + + return new ApiRequestHandlerResponse(SC_OK, resp); + } +} diff --git a/ocr/src/main/java/com/formkiq/module/ocr/Ocr.java b/ocr/src/main/java/com/formkiq/module/ocr/Ocr.java new file mode 100644 index 000000000..127da4358 --- /dev/null +++ b/ocr/src/main/java/com/formkiq/module/ocr/Ocr.java @@ -0,0 +1,215 @@ +/** + * MIT License + * + * Copyright (c) 2018 - 2020 FormKiQ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.formkiq.module.ocr; + +/** + * + * Ocr Data Holder. + * + */ +public class Ocr { + /** AddPdfDetectedCharactersAsText. */ + private boolean addPdfDetectedCharactersAsText = false; + /** Content Type. */ + private String contentType; + /** Document Id. */ + private String documentId; + /** {@link OcrEngine}. */ + private OcrEngine engine; + /** Job Id. */ + private String jobId; + /** SiteId. */ + private String siteId; + /** {@link OcrScanStatus}. */ + private OcrScanStatus status; + /** UserId. */ + private String userId; + + /** + * constructor. + */ + public Ocr() { + + } + + /** + * Is AddPdfDetectedCharactersAsText. + * + * @return boolean + */ + public boolean addPdfDetectedCharactersAsText() { + return this.addPdfDetectedCharactersAsText; + } + + /** + * Set AddPdfDetectedCharactersAsText. + * + * @param bool boolean + * @return {@link Ocr} + */ + public Ocr addPdfDetectedCharactersAsText(final boolean bool) { + this.addPdfDetectedCharactersAsText = bool; + return this; + } + + /** + * Get Content Type. + * + * @return {@link String} + */ + public String contentType() { + return this.contentType; + } + + /** + * Set Content Type. + * + * @param type {@link String} + * @return {@link Ocr} + */ + public Ocr contentType(final String type) { + this.contentType = type; + return this; + } + + /** + * Get Document Id. + * + * @return {@link String} + */ + public String documentId() { + return this.documentId; + } + + /** + * Set Document Id. + * + * @param id {@link String} + * @return {@link Ocr} + */ + public Ocr documentId(final String id) { + this.documentId = id; + return this; + } + + /** + * Get {@link OcrEngine}. + * + * @return {@link OcrEngine} + */ + public OcrEngine engine() { + return this.engine; + } + + /** + * Set {@link OcrEngine}. + * + * @param ocrEngine {@link OcrEngine} + * @return {@link Ocr} + */ + public Ocr engine(final OcrEngine ocrEngine) { + this.engine = ocrEngine; + return this; + } + + /** + * Get Job Id. + * + * @return {@link String} + */ + public String jobId() { + return this.jobId; + } + + /** + * Set Job Id. + * + * @param id {@link String} + * @return {@link Ocr} + */ + public Ocr jobId(final String id) { + this.jobId = id; + return this; + } + + /** + * Get SiteId. + * + * @return {@link String} + */ + public String siteId() { + return this.siteId; + } + + /** + * Set SiteId. + * + * @param id {@link String} + * @return {@link Ocr} + */ + public Ocr siteId(final String id) { + this.siteId = id; + return this; + } + + /** + * Get {@link OcrScanStatus}. + * + * @return {@link OcrScanStatus} + */ + public OcrScanStatus status() { + return this.status; + } + + /** + * Set {@link OcrScanStatus}. + * + * @param ocrScanStatus {@link OcrScanStatus} + * @return {@link Ocr} + */ + public Ocr status(final OcrScanStatus ocrScanStatus) { + this.status = ocrScanStatus; + return this; + } + + /** + * Get User Id. + * + * @return {@link String} + */ + public String userId() { + return this.userId; + } + + /** + * Set UserId. + * + * @param user {@link String} + * @return {@link Ocr} + */ + public Ocr userId(final String user) { + this.userId = user; + return this; + } +} diff --git a/ocr/src/main/java/com/formkiq/module/ocr/OcrEngine.java b/ocr/src/main/java/com/formkiq/module/ocr/OcrEngine.java new file mode 100644 index 000000000..e5f550289 --- /dev/null +++ b/ocr/src/main/java/com/formkiq/module/ocr/OcrEngine.java @@ -0,0 +1,38 @@ +/** + * MIT License + * + * Copyright (c) 2018 - 2020 FormKiQ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.formkiq.module.ocr; + +/** + * + * Supported OCR Engines. + * + */ +public enum OcrEngine { + /** Manually Set Ocr. */ + MANUAL, + /** Tesseract. */ + TESSERACT, + /** Textract OCR Engine. */ + TEXTRACT; +} diff --git a/ocr/src/main/java/com/formkiq/module/ocr/OcrRequest.java b/ocr/src/main/java/com/formkiq/module/ocr/OcrRequest.java new file mode 100644 index 000000000..15231a7b5 --- /dev/null +++ b/ocr/src/main/java/com/formkiq/module/ocr/OcrRequest.java @@ -0,0 +1,85 @@ +/** + * MIT License + * + * Copyright (c) 2018 - 2020 FormKiQ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.formkiq.module.ocr; + +import java.util.Collections; +import java.util.List; +import com.formkiq.graalvm.annotations.Reflectable; + +/** + * + * OCR Request. + * + */ +@Reflectable +public class OcrRequest { + + /** Add Pdf Detected Characeters as Text. */ + private boolean addPdfDetectedCharactersAsText = false; + /** Parse Types. */ + private List parseTypes; + + /** + * constructor. + */ + public OcrRequest() { + + } + + /** + * Get Parse Types. + * + * @return {@link List} {@link String} + */ + public List getParseTypes() { + return this.parseTypes != null ? Collections.unmodifiableList(this.parseTypes) : null; + } + + /** + * Is AddPdfDetectedCharactersAsText. + * + * @return boolean + */ + public boolean isAddPdfDetectedCharactersAsText() { + return this.addPdfDetectedCharactersAsText; + } + + /** + * Set AddPdfDetectedCharactersAsText. + * + * @param bool boolean + */ + public void setAddPdfDetectedCharactersAsText(final boolean bool) { + this.addPdfDetectedCharactersAsText = bool; + } + + /** + * Set Parse Types. + * + * @param types {@link List} {@link String} + */ + public void setParseTypes(final List types) { + this.parseTypes = types != null ? Collections.unmodifiableList(types) : null; + } +} diff --git a/ocr/src/main/java/com/formkiq/module/ocr/OcrScanStatus.java b/ocr/src/main/java/com/formkiq/module/ocr/OcrScanStatus.java new file mode 100644 index 000000000..399de8eb2 --- /dev/null +++ b/ocr/src/main/java/com/formkiq/module/ocr/OcrScanStatus.java @@ -0,0 +1,41 @@ +/** + * MIT License + * + * Copyright (c) 2018 - 2020 FormKiQ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.formkiq.module.ocr; + +/** + * + * OCR Scan Status. + * + */ +public enum OcrScanStatus { + /** Scanning failed. */ + FAILED, + /** Scanning for Document has been requested]. */ + REQUESTED, + /** Scanning Skipped. */ + SKIPPED, + /** Scanning was successful. */ + SUCCESSFUL; +} + diff --git a/ocr/src/main/java/com/formkiq/module/ocr/OcrSqsMessage.java b/ocr/src/main/java/com/formkiq/module/ocr/OcrSqsMessage.java new file mode 100644 index 000000000..6a9ebd685 --- /dev/null +++ b/ocr/src/main/java/com/formkiq/module/ocr/OcrSqsMessage.java @@ -0,0 +1,131 @@ +/** + * MIT License + * + * Copyright (c) 2018 - 2020 FormKiQ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.formkiq.module.ocr; + +import com.formkiq.graalvm.annotations.Reflectable; + +/** + * + * Ocr Sqs Message. + * + */ +@Reflectable +public class OcrSqsMessage { + + /** Content Type. */ + private String contentType; + /** Document Id. */ + private String documentId; + /** OCR Job Id. */ + private String jobId; + /** Site Id. */ + private String siteId; + + /** + * constructor. + */ + public OcrSqsMessage() { + + } + + /** + * Get Content-Type. + * + * @return {@link String} + */ + public String contentType() { + return this.contentType; + } + + /** + * Set Content-Type. + * + * @param type {@link String} + * @return {@link OcrSqsMessage} + */ + public OcrSqsMessage contentType(final String type) { + this.contentType = type; + return this; + } + + /** + * Get Document Id. + * + * @return {@link String} + */ + public String documentId() { + return this.documentId; + } + + /** + * Set Document Id. + * + * @param id {@link String} + * @return {@link OcrSqsMessage} + */ + public OcrSqsMessage documentId(final String id) { + this.documentId = id; + return this; + } + + /** + * Get Job Id. + * + * @return {@link String} + */ + public String jobId() { + return this.jobId; + } + + /** + * Set Job Id. + * + * @param id {@link String} + * @return {@link OcrSqsMessage} + */ + public OcrSqsMessage jobId(final String id) { + this.jobId = id; + return this; + } + + /** + * Get SiteId. + * + * @return {@link String} + */ + public String siteId() { + return this.siteId; + } + + /** + * Set Site Id. + * + * @param id {@link String} + * @return {@link OcrSqsMessage} + */ + public OcrSqsMessage siteId(final String id) { + this.siteId = id; + return this; + } +} diff --git a/ocr/src/main/java/com/formkiq/module/ocr/S3KeysNaturalComparator.java b/ocr/src/main/java/com/formkiq/module/ocr/S3KeysNaturalComparator.java new file mode 100644 index 000000000..385f00ecb --- /dev/null +++ b/ocr/src/main/java/com/formkiq/module/ocr/S3KeysNaturalComparator.java @@ -0,0 +1,66 @@ +/** + * MIT License + * + * Copyright (c) 2018 - 2020 FormKiQ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.formkiq.module.ocr; + +import java.io.Serializable; +import java.util.Comparator; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * + * {@link Comparator} That Naturally sorts Strings that end in a number. + * + * IE: test/2 should sort after test/10 + * + */ +public class S3KeysNaturalComparator implements Comparator, Serializable { + + /** Number Pattern. */ + private static final Pattern PATTERN = Pattern.compile("(.*)/(\\D*)(\\d+)$"); + /** Serial Version UID. */ + private static final long serialVersionUID = -8307896084449315528L; + + @Override + public int compare(final String s1, final String s2) { + + int result = s1.compareTo(s2); + + Matcher m1 = PATTERN.matcher(s1); + Matcher m2 = PATTERN.matcher(s2); + + if (m1.find() && m2.find()) { + + if (m1.group(1).equals(m2.group(1)) && m1.group(2).equals(m2.group(2))) { + int i = 2 + 1; + String p1 = m1.group(i); + String p2 = m2.group(i); + result = Integer.valueOf(p1).compareTo(Integer.valueOf(p2)); + + } + } + + return result; + } +} diff --git a/ocr/src/main/java/com/formkiq/module/ocr/pdf/PdfPortfolio.java b/ocr/src/main/java/com/formkiq/module/ocr/pdf/PdfPortfolio.java new file mode 100644 index 000000000..803de92b8 --- /dev/null +++ b/ocr/src/main/java/com/formkiq/module/ocr/pdf/PdfPortfolio.java @@ -0,0 +1,138 @@ +/** + * MIT License + * + * Copyright (c) 2018 - 2020 FormKiQ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.formkiq.module.ocr.pdf; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import com.itextpdf.kernel.pdf.PdfCatalog; +import com.itextpdf.kernel.pdf.PdfDictionary; +import com.itextpdf.kernel.pdf.PdfDocument; +import com.itextpdf.kernel.pdf.PdfName; +import com.itextpdf.kernel.pdf.PdfNameTree; +import com.itextpdf.kernel.pdf.PdfObject; +import com.itextpdf.kernel.pdf.PdfReader; +import com.itextpdf.kernel.pdf.PdfStream; +import com.itextpdf.kernel.pdf.PdfWriter; +import com.itextpdf.kernel.utils.PdfMerger; + +/** + * + * Helper methods for working with Pdf Portfolio. + * + */ +public class PdfPortfolio { + + /** + * Get PDF Embbeded File Names. + * + * @param pdfDoc {@link PdfDocument} + * @return {@link Map} {@link PdfObject} + */ + private static Map getEmbeddedFileNames(final PdfDocument pdfDoc) { + PdfCatalog catalog = pdfDoc.getCatalog(); + PdfNameTree embeddedFiles = catalog.getNameTree(PdfName.EmbeddedFiles); + Map names = embeddedFiles.getNames(); + return names; + } + + /** + * Get {@link List} of Pdf Portfolio Byte Streams. + * + * @param pdfDoc {@link PdfDocument} + * @return {@link List} + */ + static List getPdfPortfolioByteStreams(final PdfDocument pdfDoc) { + + Map names = getEmbeddedFileNames(pdfDoc); + + List list = new ArrayList<>(); + for (Entry entry : names.entrySet()) { + + if (entry.getValue() instanceof PdfDictionary) { + + PdfDictionary filespecDict = (PdfDictionary) entry.getValue(); + PdfDictionary embeddedFileDict = filespecDict.getAsDictionary(PdfName.EF); + PdfStream stream = embeddedFileDict.getAsStream(PdfName.UF); + if (stream == null) { + stream = embeddedFileDict.getAsStream(PdfName.F); + } + + if (stream != null) { + list.add(stream.getBytes()); + } + } + } + + return list; + } + + /** + * Is Pdf Portfolio {@link PdfDocument}. + * + * @param doc {@link PdfDocument} + * @return boolean + */ + public static boolean isPdfPortfolio(final PdfDocument doc) { + Map names = getEmbeddedFileNames(doc); + return !names.isEmpty(); + } + + /** + * Merge Multiple PDF Documents into a single {@link PdfDocument}. + * + * @param doc {@link PdfDocument} + * @param os {@link OutputStream} + * @throws IOException IOException + */ + public static void mergePdfPortfolios(final PdfDocument doc, final OutputStream os) + throws IOException { + + try (PdfWriter writer = new PdfWriter(os)) { + + try (PdfDocument document = new PdfDocument(writer)) { + PdfMerger merger = new PdfMerger(document); + + List streams = getPdfPortfolioByteStreams(doc); + for (byte[] bytes : streams) { + InputStream is = new ByteArrayInputStream(bytes); + + try (PdfReader reader = new PdfReader(is); + PdfReader setUnethicalReading = reader.setUnethicalReading(true)) { + + try (PdfDocument pdf = new PdfDocument(reader)) { + + merger.merge(pdf, 1, pdf.getNumberOfPages()); + } + } + } + } + } + } +} diff --git a/settings.gradle b/settings.gradle index c210494a1..dd7f7b726 100644 --- a/settings.gradle +++ b/settings.gradle @@ -10,6 +10,7 @@ include 'aws-cognito' include 'aws-iam' include 'aws-lambda' include 'aws-s3' +include 'aws-xray' include 'aws-sqs' include 'aws-sns' include 'aws-sts' @@ -31,3 +32,6 @@ include 'http-sigv4' include 'document-events' include 'fkq-lambda-services' include 'fkq-validation' +include 'lambda-ocr-tesseract' +include 'ocr' +include 'lambda-apikey-authorizer' \ No newline at end of file diff --git a/src/main/resources/cloudformation/template.yaml b/src/main/resources/cloudformation/template.yaml index ba2e568c8..26035c866 100644 --- a/src/main/resources/cloudformation/template.yaml +++ b/src/main/resources/cloudformation/template.yaml @@ -4,7 +4,7 @@ AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: #@ "FormKiQ Core " + data.values.version or assert.fail("missing version") - + Parameters: AdminEmail: @@ -497,7 +497,17 @@ Resources: Parameters: AppEnvironment: Ref: AppEnvironment - + + TesseractOcr: + Type: AWS::Serverless::Application + DependsOn: + - CoreApi + Properties: + Location: ocr-tesseract/template.yaml + Parameters: + AppEnvironment: + Ref: AppEnvironment + Outputs: AppEnvironment: Description: Unique Application Environment Identifier, IE dev/staging/prod @@ -509,6 +519,12 @@ Outputs: Fn::GetAtt: - CoreApi - Outputs.DocumentsIamApiUrl + KeyApiUrl: + Description: "The URL for the API endpoint that uses API Key authorization" + Value: + Fn::GetAtt: + - CoreApi + - Outputs.DocumentsKeyApiUrl HttpApiUrl: Description: "The URL for the API endpoint that uses Cognito authorization" Value: diff --git a/websocket-api/.classpath b/websocket-api/.classpath index 1b56d6287..1e4139e46 100644 --- a/websocket-api/.classpath +++ b/websocket-api/.classpath @@ -3,16 +3,28 @@ - + - + + + + + + + + + + + + + diff --git a/websocket-api/build.gradle b/websocket-api/build.gradle index 6a174dfca..af690881d 100644 --- a/websocket-api/build.gradle +++ b/websocket-api/build.gradle @@ -1,5 +1,30 @@ +import org.apache.tools.ant.taskdefs.condition.Os description = "FormKiQ Core - WebSocket" +sourceSets { + integration { + java.srcDir "$projectDir/src/integration/java" + resources.srcDir "$projectDir/src/integration/resources" + compileClasspath += main.output + test.output + runtimeClasspath += main.output + test.output + } +} + +configurations { + integrationImplementation.extendsFrom testImplementation + integrationRuntime.extendsFrom testRuntime +} + +def getCmd() { + String os = System.getProperty("os.name").toLowerCase() + return os.contains("win") ? "cmd" : "bash" +} + +def getCmdParam() { + String os = System.getProperty("os.name").toLowerCase() + return os.contains("win") ? "/c" : "-c" +} + jar.enabled = false dependencies { @@ -9,43 +34,55 @@ dependencies { testImplementation project(':aws-dynamodb') testImplementation project(':dynamodb-documents') - testImplementation group: 'software.amazon.awssdk', name: 'dynamodb', version: '2.19.2' + testImplementation group: 'software.amazon.awssdk', name: 'dynamodb', version: '2.20.33' - testImplementation group: 'junit', name: 'junit', version:'4.+' - testImplementation group: 'com.google.code.gson', name: 'gson', version: '2.10' + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version:'5.9.1' + testImplementation group: 'com.google.code.gson', name: 'gson', version: '2.10.1' testImplementation group: 'org.java-websocket', name: 'Java-WebSocket', version: '1.5.3' - testImplementation group: 'org.slf4j', name: 'slf4j-simple', version: '2.0.6' - testImplementation group: 'com.formkiq.stacks', name: 'client', version:'1.9.0' + testImplementation group: 'org.slf4j', name: 'slf4j-simple', version: '2.0.7' + testImplementation group: 'com.formkiq.stacks', name: 'client', version:'1.10.0' } test { - failFast = true - exclude 'com/formkiq/stacks/websocket/awstest/**' + failFast = true + useJUnitPlatform() } -task testaws(type: Test) { - description = 'Runs AWS integration tests.' - outputs.upToDateWhen {false} - include 'com/formkiq/stacks/websocket/awstest/**' +task integrationTest(type: Test) { + testClassesDirs = sourceSets.integration.output.classesDirs + classpath = sourceSets.integration.runtimeClasspath + useJUnitPlatform() } -task npmInstall(type:Exec) { +task npmInstall { inputs.files("src/main/resources/js") outputs.files("${buildDir}/resources/main/js/websocket-api.zip") dependsOn processResources, compileTestJava, checkstyleTest, spotbugsTest - workingDir "${buildDir}/resources/main/js" - commandLine "bash", "-c", "npm install ; npm run pack" + + doLast { + + exec { + workingDir "${buildDir}/resources/main/js" + commandLine getCmd(), getCmdParam(), "npm install" + } + + exec { + workingDir "${buildDir}/resources/main/js" + commandLine getCmd(), getCmdParam(), "npm run pack" + } + } } jar.dependsOn npmInstall task npmtest { + onlyIf { !Os.isFamily(Os.FAMILY_WINDOWS) } dependsOn npmInstall, jar inputs.files("src/main/resources/js") outputs.dir("${buildDir}/resources/main/js/coverage") doLast { exec { - commandLine "bash", "-c", "cd build/resources/main/js ; npm test" + commandLine getCmd(), getCmdParam(), "cd build/resources/main/js ; npm test" } } } @@ -68,23 +105,23 @@ task updateLambda { dependsOn assembleTemplate doLast { exec { - commandLine "bash", "-c", "aws lambda list-functions --region ${testregion} --profile ${testprofile} | jq -r '.Functions[] | .FunctionName' | grep formkiq-core-${testappenvironment} | grep 'Websocket' | xargs -I{} aws lambda update-function-code --function-name {} --zip-file fileb://${buildDir}/websocket-api.zip --region ${testregion} --profile ${testprofile}" + commandLine getCmd(), getCmdParam(), "aws lambda list-functions --region ${testregion} --profile ${testprofile} | jq -r '.Functions[] | .FunctionName' | grep formkiq-core-${testappenvironment} | grep 'Websocket' | xargs -I{} aws lambda update-function-code --function-name {} --zip-file fileb://${buildDir}/websocket-api.zip --region ${testregion} --profile ${testprofile}" } } } ext.macroLocalStackStart = { exec { - commandLine "bash", "-c", "docker-compose -f ./docker-compose.yml up -d" + commandLine getCmd(), getCmdParam(), "docker-compose -f ./docker-compose.yml up -d" } exec { - commandLine "bash", "-c", "${project.rootDir}/wait-for-localstack.sh 4568" + commandLine getCmd(), getCmdParam(), "${project.rootDir}/wait-for-localstack.sh 4568" } } ext.macroDockerComposeDown = { exec { - commandLine "bash", "-c", "docker-compose -f ${project.rootDir}/docker-compose.yml down" + commandLine getCmd(), getCmdParam(), "docker-compose -f ${project.rootDir}/docker-compose.yml down" } } diff --git a/websocket-api/config/checkstyle/mysuppressions.xml b/websocket-api/config/checkstyle/mysuppressions.xml index b311b43f1..7fb8eb829 100644 --- a/websocket-api/config/checkstyle/mysuppressions.xml +++ b/websocket-api/config/checkstyle/mysuppressions.xml @@ -4,6 +4,7 @@ + diff --git a/websocket-api/src/test/java/com/formkiq/stacks/websocket/awstest/WebSocketClientImpl.java b/websocket-api/src/integration/java/com/formkiq/stacks/websocket/awstest/WebSocketClientImpl.java similarity index 100% rename from websocket-api/src/test/java/com/formkiq/stacks/websocket/awstest/WebSocketClientImpl.java rename to websocket-api/src/integration/java/com/formkiq/stacks/websocket/awstest/WebSocketClientImpl.java diff --git a/websocket-api/src/test/java/com/formkiq/stacks/websocket/awstest/WebsocketTest.java b/websocket-api/src/integration/java/com/formkiq/stacks/websocket/awstest/WebsocketTest.java similarity index 94% rename from websocket-api/src/test/java/com/formkiq/stacks/websocket/awstest/WebsocketTest.java rename to websocket-api/src/integration/java/com/formkiq/stacks/websocket/awstest/WebsocketTest.java index a8b10a601..8a5a80726 100644 --- a/websocket-api/src/test/java/com/formkiq/stacks/websocket/awstest/WebsocketTest.java +++ b/websocket-api/src/integration/java/com/formkiq/stacks/websocket/awstest/WebsocketTest.java @@ -23,10 +23,10 @@ */ package com.formkiq.stacks.websocket.awstest; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; @@ -37,8 +37,10 @@ import java.net.http.HttpResponse.BodyHandlers; import java.util.Arrays; import java.util.Map; -import org.junit.BeforeClass; -import org.junit.Test; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; import com.formkiq.aws.cognito.CognitoConnectionBuilder; import com.formkiq.aws.cognito.CognitoService; import com.formkiq.aws.dynamodb.DynamoDbConnectionBuilder; @@ -131,7 +133,7 @@ private static void addAndLoginCognito(final String username, final String group * @throws IOException IOException * @throws URISyntaxException URISyntaxException */ - @BeforeClass + @BeforeAll public static void beforeClass() throws IOException, URISyntaxException { Region awsregion = Region.of(System.getProperty("testregion")); @@ -139,12 +141,12 @@ public static void beforeClass() throws IOException, URISyntaxException { String app = System.getProperty("testappenvironment"); SqsConnectionBuilder sqsConnection = - new SqsConnectionBuilder().setCredentials(awsprofile).setRegion(awsregion); + new SqsConnectionBuilder(false).setCredentials(awsprofile).setRegion(awsregion); sqsService = new SqsService(sqsConnection); SsmConnectionBuilder ssmBuilder = - new SsmConnectionBuilder().setCredentials(awsprofile).setRegion(awsregion); + new SsmConnectionBuilder(false).setCredentials(awsprofile).setRegion(awsregion); SsmService ssmService = new SsmServiceImpl(ssmBuilder); @@ -179,7 +181,8 @@ public static void beforeClass() throws IOException, URISyntaxException { webconnectionsTable = ssmService.getParameterValue("/formkiq/" + app + "/dynamodb/WebConnectionsTableName"); - dbConnection = new DynamoDbConnectionBuilder().setCredentials(awsprofile).setRegion(awsregion); + dbConnection = + new DynamoDbConnectionBuilder(false).setCredentials(awsprofile).setRegion(awsregion); } /** {@link HttpClient}. */ @@ -276,7 +279,8 @@ public void testConnectMissingAuthenticationHeader() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TIMEOUT) public void testConnectValidAuthentication01() throws Exception { for (Boolean useHeader : Arrays.asList(Boolean.TRUE, Boolean.FALSE)) { @@ -343,7 +347,8 @@ public void testConnectValidAuthentication01() throws Exception { * * @throws Exception Exception */ - @Test(timeout = TIMEOUT) + @Test + @Timeout(unit = TimeUnit.SECONDS, value = TIMEOUT) public void testWebNotify01() throws Exception { // given final int sleep = 500; diff --git a/websocket-api/src/main/resources/cloudformation/template.yaml b/websocket-api/src/main/resources/cloudformation/template.yaml index 5f4856f82..e969f4988 100644 --- a/websocket-api/src/main/resources/cloudformation/template.yaml +++ b/websocket-api/src/main/resources/cloudformation/template.yaml @@ -98,16 +98,11 @@ Resources: Properties: ApiId: Ref: WebSocketApi - - StageAccessLogs: - Type: AWS::Logs::LogGroup - Properties: - RetentionInDays: 90 - LogGroupName: - Fn::Sub: "/${AWS::StackName}/APIWebsocketAccessLogs" Stage: Type: AWS::ApiGatewayV2::Stage + DependsOn: + - WebSocketApi Properties: StageName: Ref: StageName @@ -116,12 +111,6 @@ Resources: Ref: Deployment ApiId: Ref: WebSocketApi - AccessLogSettings: - DestinationArn: - Fn::GetAtt: - - StageAccessLogs - - Arn - Format: '{ "requestId":"$context.requestId", "ip": "$context.identity.sourceIp", "requestTime":"$context.requestTime", "httpMethod":"$context.httpMethod","routeKey":"$context.routeKey", "status":"$context.status","protocol":"$context.protocol", "integrationStatus": $context.integrationStatus, "integrationLatency": $context.integrationLatency, "responseLength":"$context.responseLength" }' Tags: Application: Fn::Sub: "FormKiQ ${FormKiQType}" @@ -211,11 +200,13 @@ Resources: Type: AWS::Logs::LogGroup Properties: LogGroupName: - Fn::Sub: "/${AWS::StackName}/${ApiFunction}" + Fn::Sub: "/aws/vendedlogs/${AWS::StackName}/${ApiFunction}" RetentionInDays: 90 ApiFunction: Type: AWS::Serverless::Function + DependsOn: + - ApiFunctionRole Properties: Handler: index.handler Runtime: nodejs18.x