From 163aa7c546138f28015638d9922844a9c256b5cc Mon Sep 17 00:00:00 2001 From: Artsiom Korzun Date: Thu, 1 Feb 2024 17:43:25 +0100 Subject: [PATCH] support if-none-match header when putting a resource --- Dockerfile | 2 -- README.md | 3 +-- config/gflog.xml | 17 -------------- .../aidial/core/config/FileConfigStore.java | 8 ++----- .../core/controller/ResourceController.java | 18 +++++++++++++-- .../aidial/core/service/ResourceService.java | 23 +++++++++++-------- .../com/epam/aidial/core/util/HttpStatus.java | 1 + .../com/epam/aidial/core/ResourceApiTest.java | 21 ++++++++++++++--- 8 files changed, 52 insertions(+), 41 deletions(-) delete mode 100644 config/gflog.xml diff --git a/Dockerfile b/Dockerfile index ae8b103d3..be3b962df 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,7 +17,6 @@ FROM eclipse-temurin:17-jdk-alpine # TODO remove the fix once a new version is released RUN apk update && apk upgrade --no-cache libcrypto3 libssl3 -ENV JAVA_OPTS="-Dgflog.config=/app/config/gflog.xml" ENV OTEL_TRACES_EXPORTER="none" ENV OTEL_METRICS_EXPORTER="none" ENV OTEL_LOGS_EXPORTER="none" @@ -27,7 +26,6 @@ WORKDIR /app RUN adduser -u 1001 --disabled-password --gecos "" appuser COPY --from=builder --chown=appuser:appuser /build/ . -COPY --chown=appuser:appuser ./config/* /app/config/ RUN mkdir /app/log && chown -R appuser:appuser /app USER appuser diff --git a/README.md b/README.md index de55c6aa9..1a03fa7a0 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,6 @@ Static settings are used on startup and cannot be changed while application is r | storage.createBucket | false | Indicates whether bucket should be created on start-up | encryption.password | - | Password used for AES encryption | encryption.salt | - | Salt used for AES encryption -| encryption.salt | - | Salt used for AES encryption | resources.maxSize | 1048576 | Max allowed size in bytes for a resource | resources.syncPeriod | 60000 | Period in milliseconds, how frequently check for resources to sync | resources.syncDelay | 120000 | Delay in milliseconds for a resource to be written back in object storage after last modification @@ -76,7 +75,7 @@ maxmemory 4G maxmemory-policy volatile-lfu ``` -Note: Redis will be strictly required in the upcoming releases 0.7+. +Note: Redis will be strictly required in the upcoming releases 0.8+. ### Dynamic settings diff --git a/config/gflog.xml b/config/gflog.xml deleted file mode 100644 index 4f11fdef1..000000000 --- a/config/gflog.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/main/java/com/epam/aidial/core/config/FileConfigStore.java b/src/main/java/com/epam/aidial/core/config/FileConfigStore.java index a71ebfb41..a685d2e97 100644 --- a/src/main/java/com/epam/aidial/core/config/FileConfigStore.java +++ b/src/main/java/com/epam/aidial/core/config/FileConfigStore.java @@ -108,15 +108,11 @@ private void load(boolean fail) { } private Config loadConfig() throws Exception { - JsonNode tree = null; + JsonNode tree = ProxyUtil.MAPPER.createObjectNode(); for (String path : paths) { try (InputStream stream = openStream(path)) { - if (tree == null) { - tree = ProxyUtil.MAPPER.readTree(stream); - } else { - tree = ProxyUtil.MAPPER.readerForUpdating(tree).readTree(stream); - } + tree = ProxyUtil.MAPPER.readerForUpdating(tree).readTree(stream); } } diff --git a/src/main/java/com/epam/aidial/core/controller/ResourceController.java b/src/main/java/com/epam/aidial/core/controller/ResourceController.java index 71818ee1f..b49ee99dd 100644 --- a/src/main/java/com/epam/aidial/core/controller/ResourceController.java +++ b/src/main/java/com/epam/aidial/core/controller/ResourceController.java @@ -9,6 +9,7 @@ import com.epam.aidial.core.util.ProxyUtil; import io.vertx.core.Future; import io.vertx.core.Vertx; +import io.vertx.core.http.HttpHeaders; import io.vertx.core.http.HttpMethod; import lombok.extern.slf4j.Slf4j; @@ -106,6 +107,13 @@ private Future putResource(ResourceDescription descriptor) { return context.respond(HttpStatus.REQUEST_ENTITY_TOO_LARGE, message); } + String ifNoneMatch = context.getRequest().getHeader(HttpHeaders.IF_NONE_MATCH); + boolean overwrite = (ifNoneMatch == null); + + if (ifNoneMatch != null && !ifNoneMatch.equals("*")) { + return context.respond(HttpStatus.BAD_REQUEST, "only header if-none-match=* is supported"); + } + return context.getRequest().body().compose(bytes -> { if (bytes.length() > contentLimit) { String message = "Resource size: %s exceeds max limit: %s".formatted(bytes.length(), contentLimit); @@ -113,9 +121,15 @@ private Future putResource(ResourceDescription descriptor) { } String body = bytes.toString(StandardCharsets.UTF_8); - return vertx.executeBlocking(() -> service.putResource(descriptor, body)); + return vertx.executeBlocking(() -> service.putResource(descriptor, body, overwrite)); + }) + .onSuccess((metadata) -> { + if (metadata == null) { + context.respond(HttpStatus.CONFLICT, "Resource already exists: " + descriptor.getUrl()); + } else { + context.respond(HttpStatus.OK, metadata); + } }) - .onSuccess((metadata) -> context.respond(HttpStatus.OK, metadata)) .onFailure(error -> { if (error instanceof HttpException exception) { context.respond(exception.getStatus(), exception.getMessage()); diff --git a/src/main/java/com/epam/aidial/core/service/ResourceService.java b/src/main/java/com/epam/aidial/core/service/ResourceService.java index 0cd8256eb..6361112c3 100644 --- a/src/main/java/com/epam/aidial/core/service/ResourceService.java +++ b/src/main/java/com/epam/aidial/core/service/ResourceService.java @@ -67,11 +67,11 @@ public ResourceService(Vertx vertx, } /** - * @param maxSize - max allowed size in bytes for a resource. - * @param syncPeriod - period in milliseconds, how frequently check for resources to sync. - * @param syncDelay - delay in milliseconds for a resource to be written back in object storage after last modification. - * @param syncBatch - how many resources to sync in one go. - * @param cacheExpiration - expiration in milliseconds for synced resources in Redis. + * @param maxSize - max allowed size in bytes for a resource. + * @param syncPeriod - period in milliseconds, how frequently check for resources to sync. + * @param syncDelay - delay in milliseconds for a resource to be written back in object storage after last modification. + * @param syncBatch - how many resources to sync in one go. + * @param cacheExpiration - expiration in milliseconds for synced resources in Redis. * @param compressionMinSize - compress resources with gzip if their size in bytes more or equal to this value. */ public ResourceService(Vertx vertx, @@ -192,11 +192,12 @@ public String getResource(ResourceDescription descriptor, boolean lock) { return result.exists ? result.body : null; } - public ResourceItemMetadata putResource(ResourceDescription descriptor, String body) { - return putResource(descriptor, body, true); + public ResourceItemMetadata putResource(ResourceDescription descriptor, String body, boolean overwrite) { + return putResource(descriptor, body, overwrite, true); } - public ResourceItemMetadata putResource(ResourceDescription descriptor, String body, boolean lock) { + public ResourceItemMetadata putResource(ResourceDescription descriptor, String body, + boolean overwrite, boolean lock) { String redisKey = redisKey(descriptor); String blobKey = blobKey(descriptor); @@ -206,6 +207,10 @@ public ResourceItemMetadata putResource(ResourceDescription descriptor, String b result = blobGet(blobKey, false); } + if (result.exists && !overwrite) { + return null; + } + long updatedAt = time(); long createdAt = result.exists ? result.createdAt : updatedAt; redisPut(redisKey, new Result(body, createdAt, updatedAt, false, true)); @@ -224,7 +229,7 @@ public void computeResource(ResourceDescription descriptor, Function