feat(cloudfront): serve POST, PUT, PATCH and DELETE viewer requests (#4229) #4
@@ -59,6 +59,7 @@ public class CloudFrontService {
|
||||
Set.of("s3", "mediastore", "mediapackagev2", "lambda");
|
||||
private static final Set<String> OAC_SIGNING_BEHAVIORS =
|
||||
Set.of("always", "never", "no-override");
|
||||
private static final Set<String> FUNCTION_RUNTIMES = Set.of("cloudfront-js-1.0", "cloudfront-js-2.0");
|
||||
private static final int MAX_ORIGIN_CUSTOM_HEADERS = 30;
|
||||
private static final int MAX_CUSTOM_HEADER_NAME_LENGTH = 256;
|
||||
private static final int MAX_CUSTOM_HEADER_VALUE_LENGTH = 1_783;
|
||||
@@ -1227,6 +1228,7 @@ public class CloudFrontService {
|
||||
// ── CloudFront Functions ──────────────────────────────────────────────────
|
||||
|
||||
public synchronized CloudFrontFunction createFunction(CloudFrontFunction fn) {
|
||||
validateFunctionRuntime(fn.getRuntime());
|
||||
fn.setStage("DEVELOPMENT");
|
||||
fn.setStatus("UNPUBLISHED");
|
||||
fn.setEtag(UUID.randomUUID().toString());
|
||||
@@ -1251,6 +1253,7 @@ public class CloudFrontService {
|
||||
|
||||
public synchronized CloudFrontFunction updateFunction(String name, String ifMatch,
|
||||
CloudFrontFunction updated) {
|
||||
validateFunctionRuntime(updated.getRuntime());
|
||||
CloudFrontFunction existing = describeFunction(name, null);
|
||||
if (!existing.getEtag().equals(ifMatch)) {
|
||||
throw new AwsException("InvalidIfMatchVersion",
|
||||
@@ -1323,6 +1326,13 @@ public class CloudFrontService {
|
||||
return effectiveStage;
|
||||
}
|
||||
|
||||
private static void validateFunctionRuntime(String runtime) {
|
||||
if (runtime != null && !FUNCTION_RUNTIMES.contains(runtime)) {
|
||||
throw new AwsException("InvalidArgument",
|
||||
"The parameter Runtime must be cloudfront-js-1.0 or cloudfront-js-2.0.", 400);
|
||||
}
|
||||
}
|
||||
|
||||
private static AwsException invalidFunctionStage(String stage) {
|
||||
return new AwsException("InvalidArgument",
|
||||
"The parameter Stage must be DEVELOPMENT or LIVE: " + stage, 400);
|
||||
|
||||
+32
@@ -109,6 +109,38 @@ class CloudFrontManagementProtocolTest {
|
||||
equalTo("InvalidArgument")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createFunctionRejectsAnUnknownRuntime() {
|
||||
String name = "runtime-test-" + UUID.randomUUID();
|
||||
String createBody = """
|
||||
<CreateFunctionRequest xmlns="http://cloudfront.amazonaws.com/doc/2020-05-31/">
|
||||
<Name>%s</Name>
|
||||
<FunctionConfig>
|
||||
<Comment>routing function</Comment>
|
||||
<Runtime>python3</Runtime>
|
||||
</FunctionConfig>
|
||||
<FunctionCode>function handler(event) { return event.request; }</FunctionCode>
|
||||
</CreateFunctionRequest>
|
||||
""".formatted(name);
|
||||
|
||||
given()
|
||||
.contentType("application/xml")
|
||||
.body(createBody)
|
||||
.when()
|
||||
.post(API + "function")
|
||||
.then()
|
||||
.statusCode(400)
|
||||
.body(hasXPath(
|
||||
"//*[local-name()='Code']/text()",
|
||||
equalTo("InvalidArgument")));
|
||||
|
||||
given()
|
||||
.when()
|
||||
.get(API + "function/" + name + "/describe")
|
||||
.then()
|
||||
.statusCode(404);
|
||||
}
|
||||
|
||||
@Test
|
||||
void describeAndPublishFunctionPreserveBothStages() {
|
||||
String name = "describe-test-" + UUID.randomUUID();
|
||||
|
||||
+48
@@ -164,6 +164,54 @@ class CloudFrontServiceTest {
|
||||
assertNotEquals(development.getEtag(), live.getEtag());
|
||||
}
|
||||
|
||||
@Test
|
||||
void createFunction_unknownRuntime_isRejected() {
|
||||
CloudFrontService service = serviceWithDomainSuffix("cloudfront.net");
|
||||
CloudFrontFunction function = new CloudFrontFunction();
|
||||
function.setName("routing");
|
||||
function.setRuntime("python3");
|
||||
|
||||
AwsException error = assertThrows(AwsException.class, () -> service.createFunction(function));
|
||||
|
||||
assertEquals("InvalidArgument", error.getErrorCode());
|
||||
assertEquals(400, error.getHttpStatus());
|
||||
assertThrows(AwsException.class, () -> service.describeFunction("routing", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createFunction_supportedRuntimes_areAccepted() {
|
||||
CloudFrontService service = serviceWithDomainSuffix("cloudfront.net");
|
||||
for (String runtime : List.of("cloudfront-js-1.0", "cloudfront-js-2.0")) {
|
||||
CloudFrontFunction function = new CloudFrontFunction();
|
||||
function.setName("routing-" + runtime);
|
||||
function.setRuntime(runtime);
|
||||
|
||||
assertEquals(runtime, service.createFunction(function).getRuntime());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateFunction_unknownRuntime_isRejectedAndKeepsTheFunction() {
|
||||
CloudFrontService service = serviceWithDomainSuffix("cloudfront.net");
|
||||
CloudFrontFunction function = new CloudFrontFunction();
|
||||
function.setName("routing");
|
||||
function.setFunctionCode("first");
|
||||
function.setRuntime("cloudfront-js-2.0");
|
||||
String etag = service.createFunction(function).getEtag();
|
||||
CloudFrontFunction update = new CloudFrontFunction();
|
||||
update.setFunctionCode("second");
|
||||
update.setRuntime("python3");
|
||||
|
||||
AwsException error = assertThrows(AwsException.class,
|
||||
() -> service.updateFunction("routing", etag, update));
|
||||
|
||||
assertEquals("InvalidArgument", error.getErrorCode());
|
||||
CloudFrontFunction kept = service.describeFunction("routing", null);
|
||||
assertEquals("first", kept.getFunctionCode());
|
||||
assertEquals("cloudfront-js-2.0", kept.getRuntime());
|
||||
assertEquals(etag, kept.getEtag());
|
||||
}
|
||||
|
||||
@Test
|
||||
void listFunctionsWithoutStageReturnsDevelopmentAndLive() {
|
||||
CloudFrontService service = serviceWithDomainSuffix("cloudfront.net");
|
||||
|
||||
Reference in New Issue
Block a user