diff --git a/core/src/main/java/org/apache/iceberg/rest/ResourcePaths.java b/core/src/main/java/org/apache/iceberg/rest/ResourcePaths.java index 231a966f8062..de42178c9a8a 100644 --- a/core/src/main/java/org/apache/iceberg/rest/ResourcePaths.java +++ b/core/src/main/java/org/apache/iceberg/rest/ResourcePaths.java @@ -67,6 +67,8 @@ public static String tokens() { return "v1/oauth/tokens"; } + public static final String V1_EVENTS = "/v1/{prefix}/events"; + private final String prefix; private final String namespaceSeparator; @@ -151,6 +153,10 @@ public String renameView() { return SLASH.join("v1", prefix, "views", "rename"); } + public String events() { + return SLASH.join("v1", prefix, "events"); + } + public String planTableScan(TableIdentifier ident) { return SLASH.join( "v1", diff --git a/core/src/main/java/org/apache/iceberg/rest/requests/PostEventsRequest.java b/core/src/main/java/org/apache/iceberg/rest/requests/PostEventsRequest.java new file mode 100644 index 000000000000..ca56ab52a4f8 --- /dev/null +++ b/core/src/main/java/org/apache/iceberg/rest/requests/PostEventsRequest.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.iceberg.rest.requests; + +import java.util.List; +import java.util.Map; +import org.apache.iceberg.rest.RESTRequest; +import org.immutables.value.Value; + +@Value.Immutable +public interface PostEventsRequest extends RESTRequest { + + List> events(); + + @Override + default void validate() { + // nothing to validate for test harness + } +} diff --git a/core/src/main/java/org/apache/iceberg/rest/responses/EventsResponse.java b/core/src/main/java/org/apache/iceberg/rest/responses/EventsResponse.java new file mode 100644 index 000000000000..43bdc5a5193b --- /dev/null +++ b/core/src/main/java/org/apache/iceberg/rest/responses/EventsResponse.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.iceberg.rest.responses; + +import java.util.List; +import java.util.Map; +import org.apache.iceberg.rest.RESTResponse; +import org.immutables.value.Value; + +@Value.Immutable +public interface EventsResponse extends RESTResponse { + + List> events(); + + static EventsResponse of(List> events) { + return ImmutableEventsResponse.builder().addAllEvents(events).build(); + } +} diff --git a/core/src/test/java/org/apache/iceberg/rest/RESTCatalogAdapter.java b/core/src/test/java/org/apache/iceberg/rest/RESTCatalogAdapter.java index 524b3e760ca6..1307402a3b31 100644 --- a/core/src/test/java/org/apache/iceberg/rest/RESTCatalogAdapter.java +++ b/core/src/test/java/org/apache/iceberg/rest/RESTCatalogAdapter.java @@ -71,6 +71,7 @@ import org.apache.iceberg.rest.requests.RegisterTableRequest; import org.apache.iceberg.rest.requests.RenameTableRequest; import org.apache.iceberg.rest.requests.ReportMetricsRequest; +import org.apache.iceberg.rest.requests.PostEventsRequest; import org.apache.iceberg.rest.requests.UpdateNamespacePropertiesRequest; import org.apache.iceberg.rest.requests.UpdateTableRequest; import org.apache.iceberg.rest.responses.ConfigResponse; @@ -78,6 +79,7 @@ import org.apache.iceberg.rest.responses.LoadTableResponse; import org.apache.iceberg.rest.responses.OAuthTokenResponse; import org.apache.iceberg.util.Pair; +import org.apache.iceberg.rest.events.InMemoryEventsStore; import org.apache.iceberg.util.PropertyUtil; /** Adaptor class to translate REST requests into {@link Catalog} API calls. */ @@ -114,6 +116,8 @@ public class RESTCatalogAdapter extends BaseHTTPClient { private AuthSession authSession = AuthSession.EMPTY; private PlanningBehavior planningBehavior; + // single in-memory store used by test REST server + private static final InMemoryEventsStore EVENTS_STORE = new InMemoryEventsStore(); public RESTCatalogAdapter(Catalog catalog) { this.catalog = catalog; @@ -388,6 +392,18 @@ public T handleRequest( // nothing to do here other than checking that we're getting the correct request castRequest(ReportMetricsRequest.class, body); return null; + + case EVENTS_POST: + { + PostEventsRequest request = castRequest(PostEventsRequest.class, body); + EVENTS_STORE.postEvents(request.events()); + return castResponse(responseType, EventsResponse.of(EVENTS_STORE.getEvents())); + } + + case EVENTS_GET: + { + return castResponse(responseType, EventsResponse.of(EVENTS_STORE.getEvents())); + } } case COMMIT_TRANSACTION: diff --git a/core/src/test/java/org/apache/iceberg/rest/Route.java b/core/src/test/java/org/apache/iceberg/rest/Route.java index eedb2615ad64..1c3c7050ef21 100644 --- a/core/src/test/java/org/apache/iceberg/rest/Route.java +++ b/core/src/test/java/org/apache/iceberg/rest/Route.java @@ -30,6 +30,7 @@ import org.apache.iceberg.rest.requests.PlanTableScanRequest; import org.apache.iceberg.rest.requests.RegisterTableRequest; import org.apache.iceberg.rest.requests.RenameTableRequest; +import org.apache.iceberg.rest.requests.PostEventsRequest; import org.apache.iceberg.rest.requests.ReportMetricsRequest; import org.apache.iceberg.rest.requests.UpdateNamespacePropertiesRequest; import org.apache.iceberg.rest.requests.UpdateTableRequest; @@ -44,6 +45,7 @@ import org.apache.iceberg.rest.responses.LoadViewResponse; import org.apache.iceberg.rest.responses.OAuthTokenResponse; import org.apache.iceberg.rest.responses.PlanTableScanResponse; +import org.apache.iceberg.rest.responses.EventsResponse; import org.apache.iceberg.rest.responses.UpdateNamespacePropertiesResponse; import org.apache.iceberg.util.Pair; @@ -130,8 +132,13 @@ enum Route { ResourcePaths.V1_TABLE_SCAN_PLAN_TASKS, FetchScanTasksRequest.class, FetchScanTasksResponse.class), - CANCEL_PLAN_TABLE_SCAN( - HTTPRequest.HTTPMethod.DELETE, ResourcePaths.V1_TABLE_SCAN_PLAN, null, null); + CANCEL_PLAN_TABLE_SCAN( + HTTPRequest.HTTPMethod.DELETE, ResourcePaths.V1_TABLE_SCAN_PLAN, null, null), + + EVENTS_POST( + HTTPRequest.HTTPMethod.POST, ResourcePaths.V1_EVENTS, PostEventsRequest.class, EventsResponse.class), + + EVENTS_GET(HTTPRequest.HTTPMethod.GET, ResourcePaths.V1_EVENTS, null, EventsResponse.class); private final HTTPRequest.HTTPMethod method; private final int requiredLength; diff --git a/core/src/test/java/org/apache/iceberg/rest/TestEventsEndpoint.java b/core/src/test/java/org/apache/iceberg/rest/TestEventsEndpoint.java new file mode 100644 index 000000000000..c6068a6a630d --- /dev/null +++ b/core/src/test/java/org/apache/iceberg/rest/TestEventsEndpoint.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.iceberg.rest; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import org.apache.iceberg.rest.requests.PostEventsRequest; +import org.apache.iceberg.rest.responses.EventsResponse; +import org.junit.Test; + +public class TestEventsEndpoint { + + @Test + public void testPostAndGetEvents() { + RESTCatalogAdapter adapter = new RESTCatalogAdapter(null) {}; + + PostEventsRequest req = ImmutablePostEventsRequest.builder().addAllEvents(Collections.emptyList()).build(); + + EventsResponse postResp = adapter.handleRequest(Route.EVENTS_POST, Collections.emptyMap(), + ImmutableHTTPRequest.builder().method(HTTPRequest.HTTPMethod.POST).path("v1/test/events").build(), EventsResponse.class, headers -> {}); + + assertThat(postResp).isNotNull(); + + EventsResponse getResp = adapter.handleRequest(Route.EVENTS_GET, Collections.emptyMap(), + ImmutableHTTPRequest.builder().method(HTTPRequest.HTTPMethod.GET).path("v1/test/events").build(), EventsResponse.class, headers -> {}); + + assertThat(getResp).isNotNull(); + assertThat(getResp.events()).isNotNull(); + } +} diff --git a/core/src/test/java/org/apache/iceberg/rest/events/InMemoryEventsStore.java b/core/src/test/java/org/apache/iceberg/rest/events/InMemoryEventsStore.java new file mode 100644 index 000000000000..28922813b47b --- /dev/null +++ b/core/src/test/java/org/apache/iceberg/rest/events/InMemoryEventsStore.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.iceberg.rest.events; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; + +/** Simple in-memory events store for test harness use. */ +public class InMemoryEventsStore { + private final List> events = new CopyOnWriteArrayList<>(); + + public void postEvents(List> evts) { + if (evts != null && !evts.isEmpty()) { + events.addAll(evts); + } + } + + public List> getEvents() { + return Collections.unmodifiableList(new ArrayList<>(events)); + } +} diff --git a/core/src/test/java/org/apache/iceberg/rest/requests/PostEventsRequest.java b/core/src/test/java/org/apache/iceberg/rest/requests/PostEventsRequest.java new file mode 100644 index 000000000000..ca56ab52a4f8 --- /dev/null +++ b/core/src/test/java/org/apache/iceberg/rest/requests/PostEventsRequest.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.iceberg.rest.requests; + +import java.util.List; +import java.util.Map; +import org.apache.iceberg.rest.RESTRequest; +import org.immutables.value.Value; + +@Value.Immutable +public interface PostEventsRequest extends RESTRequest { + + List> events(); + + @Override + default void validate() { + // nothing to validate for test harness + } +}