From 538acbf295d2cf6fdb642c3931ae0fa8df6e8e4a Mon Sep 17 00:00:00 2001 From: Aaron Sequeira <96731649+aaronseq12@users.noreply.github.com> Date: Fri, 10 Oct 2025 19:44:52 +0300 Subject: [PATCH 1/4] docs: Add documentation for custom header support in Table Topic catalog configuration This documentation explains how to configure custom HTTP headers for REST catalogs in AutoMQ Table Topic, specifically addressing Apache Polaris integration needs. Closes #2890 --- docs/table-topic-custom-headers.md | 184 +++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 docs/table-topic-custom-headers.md diff --git a/docs/table-topic-custom-headers.md b/docs/table-topic-custom-headers.md new file mode 100644 index 0000000000..279ae78098 --- /dev/null +++ b/docs/table-topic-custom-headers.md @@ -0,0 +1,184 @@ +# Custom Headers Configuration for Table Topic Catalogs + +AutoMQ Table Topic supports custom HTTP headers for REST catalog integrations, enabling seamless integration with catalog services that require specific headers such as Apache Polaris, authentication tokens, or tenant identification. + +## Overview + +When using Table Topic with REST-based Iceberg catalogs, you can configure custom HTTP headers that will be included in all catalog requests. This is particularly useful for: + +- **Apache Polaris** integration requiring `Polaris-Realm` headers +- **Authentication** using custom bearer tokens or API keys +- **Multi-tenant** environments requiring tenant identification headers +- **Custom routing** or load balancing headers + +## Configuration Pattern + +Custom headers are configured using the following pattern: + +```properties +automq.table.topic.catalog.header.{header-name}={header-value} +``` + +Where: +- `{header-name}` is the HTTP header name (case-sensitive) +- `{header-value}` is the header value + +## Apache Polaris Integration + +[Apache Polaris](https://polaris.apache.org/) requires realm-specific headers for multi-tenant environments. Configure AutoMQ to work with Polaris as follows: + +```properties +# Basic REST catalog configuration +automq.table.topic.catalog.type=rest +automq.table.topic.catalog.uri=http://your-polaris-server:8181 +automq.table.topic.catalog.warehouse=s3://your-bucket/iceberg + +# Polaris-specific realm header +automq.table.topic.catalog.header.Polaris-Realm=your-realm-name + +# Optional: Additional Polaris headers +automq.table.topic.catalog.header.Authorization=Bearer your-token +``` + +### Polaris Configuration Details + +According to the [Polaris production configuration guide](https://polaris.apache.org/releases/1.1.0/configuring-polaris-for-production/): + +- If a request contains the `Polaris-Realm` header, Polaris uses the specified realm +- If the realm is not in the allowed list, Polaris returns a 404 Not Found response +- If no header is present, Polaris uses the first realm in the list as default + +With AutoMQ's header configuration, you can explicitly set the realm: + +```properties +automq.table.topic.catalog.header.Polaris-Realm=PRODUCTION +``` + +## Common Use Cases + +### Authentication Headers + +```properties +# Bearer token authentication +automq.table.topic.catalog.header.Authorization=Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... + +# API key authentication +automq.table.topic.catalog.header.X-API-Key=your-api-key +``` + +### Multi-tenant Environments + +```properties +# Tenant identification +automq.table.topic.catalog.header.X-Tenant-ID=tenant-123 +automq.table.topic.catalog.header.X-Organization=my-org +``` + +### Custom Routing + +```properties +# Load balancer routing +automq.table.topic.catalog.header.X-Route-To=datacenter-west +automq.table.topic.catalog.header.X-Service-Version=v2 +``` + +### Content Type and Accept Headers + +```properties +# Explicit content type (usually handled automatically) +automq.table.topic.catalog.header.Content-Type=application/json +automq.table.topic.catalog.header.Accept=application/json +``` + +## Complete Configuration Example + +Here's a comprehensive example combining multiple header types: + +```properties +# S3 and general configuration +s3.data.buckets=0@s3://my-bucket?region=us-east-1&endpoint=http://localhost:9000 + +# Table Topic catalog configuration +automq.table.topic.catalog.type=rest +automq.table.topic.catalog.uri=http://polaris-server:8181 +automq.table.topic.catalog.warehouse=s3://my-bucket/iceberg + +# Custom headers for Polaris integration +automq.table.topic.catalog.header.Polaris-Realm=PRODUCTION +automq.table.topic.catalog.header.Authorization=Bearer your-jwt-token +automq.table.topic.catalog.header.X-Tenant-ID=tenant-production +automq.table.topic.catalog.header.X-Client-Version=automq-1.6.0 +``` + +## Supported Catalog Types + +Custom headers are supported for the following catalog types: + +- ✅ **REST** (`automq.table.topic.catalog.type=rest`) - Full support +- ❌ **AWS Glue** (`automq.table.topic.catalog.type=glue`) - Not applicable (uses AWS SDK) +- ❌ **Hive Metastore** (`automq.table.topic.catalog.type=hive`) - Not applicable (uses Thrift) +- ❌ **Nessie** (`automq.table.topic.catalog.type=nessie`) - Uses REST but may have specific auth mechanisms +- ❌ **Table Bucket** (`automq.table.topic.catalog.type=tablebucket`) - Uses AWS SDK + +> **Note**: Custom headers are primarily designed for REST catalog integrations where HTTP headers are the standard mechanism for metadata and authentication. + +## Troubleshooting + +### Header Not Being Sent + +1. **Verify Configuration**: Ensure the header configuration follows the exact pattern: + ```properties + automq.table.topic.catalog.header.Header-Name=header-value + ``` + +2. **Check Catalog Type**: Custom headers only work with `type=rest` + +3. **Case Sensitivity**: HTTP header names are case-sensitive. Use the exact casing required by your catalog service. + +### Polaris Returns 404 Not Found + +1. **Verify Realm Name**: Ensure the realm name in the header matches exactly with your Polaris configuration +2. **Check Allowed Realms**: Verify that the realm is in Polaris's allowed realms list +3. **Network Connectivity**: Ensure AutoMQ can reach the Polaris server + +### Authentication Failures + +1. **Token Expiry**: Check if your bearer token or API key has expired +2. **Token Format**: Ensure the token format matches what your catalog expects +3. **Permissions**: Verify the token has the necessary permissions for catalog operations + +## Implementation Details + +Custom headers are implemented in the `CatalogFactory` class and are passed through to the underlying Apache Iceberg REST catalog implementation. The headers are included in all HTTP requests made to the catalog service, including: + +- Configuration requests (`GET /v1/config`) +- Namespace operations (`GET /v1/namespaces`, `POST /v1/namespaces`) +- Table operations (`GET /v1/namespaces/{namespace}/tables`, `POST /v1/namespaces/{namespace}/tables`) +- Metadata operations + +## Testing Your Configuration + +To verify that your custom headers are working correctly: + +1. **Enable Debug Logging**: Add the following to your log4j configuration: + ```properties + log4j.logger.org.apache.iceberg.rest=DEBUG + ``` + +2. **Monitor Network Traffic**: Use tools like `tcpdump` or Wireshark to inspect HTTP requests + +3. **Check Catalog Server Logs**: Review your catalog server logs to ensure headers are being received + +4. **Test with curl**: Manually test your catalog endpoint with the same headers: + ```bash + curl -H "Polaris-Realm: your-realm" \ + -H "Authorization: Bearer your-token" \ + "http://your-polaris-server:8181/v1/config?warehouse=s3://bucket/iceberg" + ``` + +## References + +- [Apache Polaris Documentation](https://polaris.apache.org/) +- [Apache Iceberg REST Catalog Specification](https://iceberg.apache.org/docs/latest/rest/) +- [AutoMQ Table Topic Documentation](https://www.automq.com/docs/automq/table-topic/) +- [GitHub Issue #2890](https://github.com/AutoMQ/automq/issues/2890) From 2c87d4f7409404d6dd9c284ab67319ebdd8bbca9 Mon Sep 17 00:00:00 2001 From: Aaron Sequeira <96731649+aaronseq12@users.noreply.github.com> Date: Fri, 10 Oct 2025 19:46:46 +0300 Subject: [PATCH 2/4] test: Add specific test case for Apache Polaris realm header support Adds a test case that demonstrates the Polaris-Realm header configuration functionality, addressing the specific use case mentioned in issue #2890. --- .../automq/table/CatalogFactoryTest.java | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/core/src/test/java/kafka/automq/table/CatalogFactoryTest.java b/core/src/test/java/kafka/automq/table/CatalogFactoryTest.java index 9ef3e531b0..a7701fc94f 100644 --- a/core/src/test/java/kafka/automq/table/CatalogFactoryTest.java +++ b/core/src/test/java/kafka/automq/table/CatalogFactoryTest.java @@ -55,6 +55,23 @@ void restPassthroughProperties() throws IOException { assertEquals(List.of("GET /v1/config?warehouse=s3://my_bucket/iceberg\nmy-x"), restCatalog.requests()); } + @Test + void restWithPolarisRealmHeader() throws IOException { + final var restCatalog = new RestCatalogMockForPolaris(); + try (final var autoClose = restCatalog) { + final var config = new KafkaConfig(merge(requiredKafkaConfigProperties, Map.of( + "automq.table.topic.catalog.type", "rest", + "automq.table.topic.catalog.uri", restCatalog.base(), + "automq.table.topic.catalog.header.Polaris-Realm", "PRODUCTION", // Apache Polaris realm header + "automq.table.topic.catalog.header.Authorization", "Bearer test-token", // Authentication header + "s3.data.buckets", "0@s3://my_bucket?region=us-east-1&endpoint=http://localhost:12345&pathStyle=true" + ))); + final var catalog = new CatalogFactory.Builder(config).build(); + assertInstanceOf(RESTCatalog.class, catalog).close(); + } + assertEquals(List.of("GET /v1/config?warehouse=s3://my_bucket/iceberg\nPRODUCTION\nBearer test-token"), restCatalog.requests()); + } + @Test void ignoreEmptyS3EndpointForRestCatalog() throws IOException { FakeS3IO.lastS3FileIOProperties = null; @@ -143,4 +160,55 @@ public void close() { catalogBackend.stop(0); } } + + private static class RestCatalogMockForPolaris implements AutoCloseable { + private final List requests = new CopyOnWriteArrayList<>(); + private final HttpServer catalogBackend; + + private RestCatalogMockForPolaris() throws IOException { + catalogBackend = HttpServer.create(new InetSocketAddress("localhost", 0), 16); + catalogBackend.createContext("/").setHandler(ex -> { + try (ex) { + final var method = ex.getRequestMethod(); + requests.add( + method + ' ' + ex.getRequestURI().getPath() + '?' + ex.getRequestURI().getQuery() + + ('\n' + String.join("", ex.getRequestHeaders().getOrDefault("Polaris-Realm", List.of()))) + + ('\n' + String.join("", ex.getRequestHeaders().getOrDefault("Authorization", List.of()))) + + ('\n' + new String(ex.getRequestBody().readAllBytes(), UTF_8)).strip()); + + if (method.equals("GET") && + ex.getRequestURI().getPath().equals("/v1/config") && + "warehouse=s3%3A%2F%2Fmy_bucket%2Ficeberg".equals(ex.getRequestURI().getRawQuery())) { + final var body = """ + { + "defaults": {}, + "overrides": {} + } + """.getBytes(UTF_8); + ex.getResponseHeaders().add("content-type", "application/json"); + ex.sendResponseHeaders(200, body.length); + ex.getResponseBody().write(body); + return; + } + + // else we just called an unexpected endpoint, issue a HTTP 404 + ex.sendResponseHeaders(404, 0); + } + }); + catalogBackend.start(); + } + + private String base() { + return "http://localhost:" + catalogBackend.getAddress().getPort(); + } + + private List requests() { + return requests; + } + + @Override + public void close() { + catalogBackend.stop(0); + } + } } From e965a0603b770348cbfeff6cf3b0d28a6a15c878 Mon Sep 17 00:00:00 2001 From: Aaron Sequeira <96731649+aaron-seq@users.noreply.github.com> Date: Sun, 12 Oct 2025 10:56:32 +0300 Subject: [PATCH 3/4] refactor: Remove redundant Polaris test case to avoid duplication The restPassthroughProperties() test already validates custom header functionality. Removing restWithPolarisRealmHeader() and RestCatalogMockForPolaris to avoid test redundancy as suggested in code review feedback. --- .../automq/table/CatalogFactoryTest.java | 70 +------------------ 1 file changed, 1 insertion(+), 69 deletions(-) diff --git a/core/src/test/java/kafka/automq/table/CatalogFactoryTest.java b/core/src/test/java/kafka/automq/table/CatalogFactoryTest.java index a7701fc94f..af70722a84 100644 --- a/core/src/test/java/kafka/automq/table/CatalogFactoryTest.java +++ b/core/src/test/java/kafka/automq/table/CatalogFactoryTest.java @@ -55,23 +55,6 @@ void restPassthroughProperties() throws IOException { assertEquals(List.of("GET /v1/config?warehouse=s3://my_bucket/iceberg\nmy-x"), restCatalog.requests()); } - @Test - void restWithPolarisRealmHeader() throws IOException { - final var restCatalog = new RestCatalogMockForPolaris(); - try (final var autoClose = restCatalog) { - final var config = new KafkaConfig(merge(requiredKafkaConfigProperties, Map.of( - "automq.table.topic.catalog.type", "rest", - "automq.table.topic.catalog.uri", restCatalog.base(), - "automq.table.topic.catalog.header.Polaris-Realm", "PRODUCTION", // Apache Polaris realm header - "automq.table.topic.catalog.header.Authorization", "Bearer test-token", // Authentication header - "s3.data.buckets", "0@s3://my_bucket?region=us-east-1&endpoint=http://localhost:12345&pathStyle=true" - ))); - final var catalog = new CatalogFactory.Builder(config).build(); - assertInstanceOf(RESTCatalog.class, catalog).close(); - } - assertEquals(List.of("GET /v1/config?warehouse=s3://my_bucket/iceberg\nPRODUCTION\nBearer test-token"), restCatalog.requests()); - } - @Test void ignoreEmptyS3EndpointForRestCatalog() throws IOException { FakeS3IO.lastS3FileIOProperties = null; @@ -160,55 +143,4 @@ public void close() { catalogBackend.stop(0); } } - - private static class RestCatalogMockForPolaris implements AutoCloseable { - private final List requests = new CopyOnWriteArrayList<>(); - private final HttpServer catalogBackend; - - private RestCatalogMockForPolaris() throws IOException { - catalogBackend = HttpServer.create(new InetSocketAddress("localhost", 0), 16); - catalogBackend.createContext("/").setHandler(ex -> { - try (ex) { - final var method = ex.getRequestMethod(); - requests.add( - method + ' ' + ex.getRequestURI().getPath() + '?' + ex.getRequestURI().getQuery() + - ('\n' + String.join("", ex.getRequestHeaders().getOrDefault("Polaris-Realm", List.of()))) + - ('\n' + String.join("", ex.getRequestHeaders().getOrDefault("Authorization", List.of()))) + - ('\n' + new String(ex.getRequestBody().readAllBytes(), UTF_8)).strip()); - - if (method.equals("GET") && - ex.getRequestURI().getPath().equals("/v1/config") && - "warehouse=s3%3A%2F%2Fmy_bucket%2Ficeberg".equals(ex.getRequestURI().getRawQuery())) { - final var body = """ - { - "defaults": {}, - "overrides": {} - } - """.getBytes(UTF_8); - ex.getResponseHeaders().add("content-type", "application/json"); - ex.sendResponseHeaders(200, body.length); - ex.getResponseBody().write(body); - return; - } - - // else we just called an unexpected endpoint, issue a HTTP 404 - ex.sendResponseHeaders(404, 0); - } - }); - catalogBackend.start(); - } - - private String base() { - return "http://localhost:" + catalogBackend.getAddress().getPort(); - } - - private List requests() { - return requests; - } - - @Override - public void close() { - catalogBackend.stop(0); - } - } -} +} \ No newline at end of file From d59bf302d4df76e3ce8f77b440ec01e7b71c7482 Mon Sep 17 00:00:00 2001 From: Aaron Sequeira <96731649+aaron-seq@users.noreply.github.com> Date: Sun, 12 Oct 2025 10:58:51 +0300 Subject: [PATCH 4/4] docs: Simplify header documentation and point to automq-labs Simplified documentation to focus on configuration pattern and point users to automq-labs repository for detailed integration examples as suggested in code review feedback. --- docs/table-topic-custom-headers.md | 176 ++++------------------------- 1 file changed, 20 insertions(+), 156 deletions(-) diff --git a/docs/table-topic-custom-headers.md b/docs/table-topic-custom-headers.md index 279ae78098..e67867b05f 100644 --- a/docs/table-topic-custom-headers.md +++ b/docs/table-topic-custom-headers.md @@ -1,19 +1,10 @@ -# Custom Headers Configuration for Table Topic Catalogs +# Custom Headers for Table Topic REST Catalogs -AutoMQ Table Topic supports custom HTTP headers for REST catalog integrations, enabling seamless integration with catalog services that require specific headers such as Apache Polaris, authentication tokens, or tenant identification. - -## Overview - -When using Table Topic with REST-based Iceberg catalogs, you can configure custom HTTP headers that will be included in all catalog requests. This is particularly useful for: - -- **Apache Polaris** integration requiring `Polaris-Realm` headers -- **Authentication** using custom bearer tokens or API keys -- **Multi-tenant** environments requiring tenant identification headers -- **Custom routing** or load balancing headers +AutoMQ Table Topic supports custom HTTP headers for REST catalog integrations, enabling integration with catalog services that require specific headers. ## Configuration Pattern -Custom headers are configured using the following pattern: +Custom headers can be configured using the following pattern: ```properties automq.table.topic.catalog.header.{header-name}={header-value} @@ -23,162 +14,35 @@ Where: - `{header-name}` is the HTTP header name (case-sensitive) - `{header-value}` is the header value -## Apache Polaris Integration +## Common Use Cases + +- **Authentication**: Bearer tokens, API keys +- **Multi-tenant environments**: Tenant identification headers +- **Apache Polaris integration**: Realm-specific headers +- **Custom routing**: Load balancer or service routing headers -[Apache Polaris](https://polaris.apache.org/) requires realm-specific headers for multi-tenant environments. Configure AutoMQ to work with Polaris as follows: +## Example Configuration ```properties # Basic REST catalog configuration automq.table.topic.catalog.type=rest -automq.table.topic.catalog.uri=http://your-polaris-server:8181 +automq.table.topic.catalog.uri=http://your-catalog-server:8181 automq.table.topic.catalog.warehouse=s3://your-bucket/iceberg -# Polaris-specific realm header -automq.table.topic.catalog.header.Polaris-Realm=your-realm-name - -# Optional: Additional Polaris headers +# Custom headers automq.table.topic.catalog.header.Authorization=Bearer your-token +automq.table.topic.catalog.header.X-Custom-Header=custom-value ``` -### Polaris Configuration Details - -According to the [Polaris production configuration guide](https://polaris.apache.org/releases/1.1.0/configuring-polaris-for-production/): - -- If a request contains the `Polaris-Realm` header, Polaris uses the specified realm -- If the realm is not in the allowed list, Polaris returns a 404 Not Found response -- If no header is present, Polaris uses the first realm in the list as default - -With AutoMQ's header configuration, you can explicitly set the realm: - -```properties -automq.table.topic.catalog.header.Polaris-Realm=PRODUCTION -``` - -## Common Use Cases - -### Authentication Headers - -```properties -# Bearer token authentication -automq.table.topic.catalog.header.Authorization=Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... - -# API key authentication -automq.table.topic.catalog.header.X-API-Key=your-api-key -``` - -### Multi-tenant Environments - -```properties -# Tenant identification -automq.table.topic.catalog.header.X-Tenant-ID=tenant-123 -automq.table.topic.catalog.header.X-Organization=my-org -``` +## Integration Examples -### Custom Routing - -```properties -# Load balancer routing -automq.table.topic.catalog.header.X-Route-To=datacenter-west -automq.table.topic.catalog.header.X-Service-Version=v2 -``` - -### Content Type and Accept Headers - -```properties -# Explicit content type (usually handled automatically) -automq.table.topic.catalog.header.Content-Type=application/json -automq.table.topic.catalog.header.Accept=application/json -``` +For detailed integration examples, including: +- Apache Polaris setup with docker-compose +- Authentication configurations +- Troubleshooting guides -## Complete Configuration Example - -Here's a comprehensive example combining multiple header types: - -```properties -# S3 and general configuration -s3.data.buckets=0@s3://my-bucket?region=us-east-1&endpoint=http://localhost:9000 - -# Table Topic catalog configuration -automq.table.topic.catalog.type=rest -automq.table.topic.catalog.uri=http://polaris-server:8181 -automq.table.topic.catalog.warehouse=s3://my-bucket/iceberg - -# Custom headers for Polaris integration -automq.table.topic.catalog.header.Polaris-Realm=PRODUCTION -automq.table.topic.catalog.header.Authorization=Bearer your-jwt-token -automq.table.topic.catalog.header.X-Tenant-ID=tenant-production -automq.table.topic.catalog.header.X-Client-Version=automq-1.6.0 -``` +Visit the [AutoMQ Labs repository](https://github.com/AutoMQ/automq-labs/tree/main/table-topic-solutions). ## Supported Catalog Types -Custom headers are supported for the following catalog types: - -- ✅ **REST** (`automq.table.topic.catalog.type=rest`) - Full support -- ❌ **AWS Glue** (`automq.table.topic.catalog.type=glue`) - Not applicable (uses AWS SDK) -- ❌ **Hive Metastore** (`automq.table.topic.catalog.type=hive`) - Not applicable (uses Thrift) -- ❌ **Nessie** (`automq.table.topic.catalog.type=nessie`) - Uses REST but may have specific auth mechanisms -- ❌ **Table Bucket** (`automq.table.topic.catalog.type=tablebucket`) - Uses AWS SDK - -> **Note**: Custom headers are primarily designed for REST catalog integrations where HTTP headers are the standard mechanism for metadata and authentication. - -## Troubleshooting - -### Header Not Being Sent - -1. **Verify Configuration**: Ensure the header configuration follows the exact pattern: - ```properties - automq.table.topic.catalog.header.Header-Name=header-value - ``` - -2. **Check Catalog Type**: Custom headers only work with `type=rest` - -3. **Case Sensitivity**: HTTP header names are case-sensitive. Use the exact casing required by your catalog service. - -### Polaris Returns 404 Not Found - -1. **Verify Realm Name**: Ensure the realm name in the header matches exactly with your Polaris configuration -2. **Check Allowed Realms**: Verify that the realm is in Polaris's allowed realms list -3. **Network Connectivity**: Ensure AutoMQ can reach the Polaris server - -### Authentication Failures - -1. **Token Expiry**: Check if your bearer token or API key has expired -2. **Token Format**: Ensure the token format matches what your catalog expects -3. **Permissions**: Verify the token has the necessary permissions for catalog operations - -## Implementation Details - -Custom headers are implemented in the `CatalogFactory` class and are passed through to the underlying Apache Iceberg REST catalog implementation. The headers are included in all HTTP requests made to the catalog service, including: - -- Configuration requests (`GET /v1/config`) -- Namespace operations (`GET /v1/namespaces`, `POST /v1/namespaces`) -- Table operations (`GET /v1/namespaces/{namespace}/tables`, `POST /v1/namespaces/{namespace}/tables`) -- Metadata operations - -## Testing Your Configuration - -To verify that your custom headers are working correctly: - -1. **Enable Debug Logging**: Add the following to your log4j configuration: - ```properties - log4j.logger.org.apache.iceberg.rest=DEBUG - ``` - -2. **Monitor Network Traffic**: Use tools like `tcpdump` or Wireshark to inspect HTTP requests - -3. **Check Catalog Server Logs**: Review your catalog server logs to ensure headers are being received - -4. **Test with curl**: Manually test your catalog endpoint with the same headers: - ```bash - curl -H "Polaris-Realm: your-realm" \ - -H "Authorization: Bearer your-token" \ - "http://your-polaris-server:8181/v1/config?warehouse=s3://bucket/iceberg" - ``` - -## References - -- [Apache Polaris Documentation](https://polaris.apache.org/) -- [Apache Iceberg REST Catalog Specification](https://iceberg.apache.org/docs/latest/rest/) -- [AutoMQ Table Topic Documentation](https://www.automq.com/docs/automq/table-topic/) -- [GitHub Issue #2890](https://github.com/AutoMQ/automq/issues/2890) +Custom headers are supported only for REST catalogs (`automq.table.topic.catalog.type=rest`). \ No newline at end of file