Repository navigation
dbeaver/pro#11108 Add External DAM transport contracts and backend client #16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
vkirst
wants to merge
22
commits into
devel
Choose a base branch
from
dbeaver/pro#11108-desktop-websocket-transport
base: devel
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+12,546
−0
Open
Changes from all commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
f5cd34f
dbeaver/pro#11108 Add external DAM transport contracts
vkirst 1a15585
dbeaver/pro#11108 Add service-authenticated External DAM GraphQL client
vkirst 2b04e9b
dbeaver/pro#11108 Fix External DAM API Checkstyle warnings
vkirst c5ec67d
dbeaver/pro#11108 Fix External DAM protocol separation and OSGi visib…
vkirst 34b34ea
dbeaver/pro#11108 Add backend subscription clients and channel status…
vkirst e39ab29
dbeaver/pro#11108 Add transport-independent request/reply messaging
vkirst 21477d0
dbeaver/pro#11108 Add missing interface Javadoc
vkirst 6edb503
dbeaver/pro#11108 Add transport-independent typed RPC
vkirst 7343246
dbeaver/pro#11108 Add internal connection account lookup
vkirst 3b8802b
dbeaver/pro#11108 Add per-binding codecs and direct RPC topics
vkirst e289d46
dbeaver/pro#11108 Add dynamic subscription callback metadata
vkirst 601b5dc
dbeaver/pro#11108 Preserve typed transport send failures
vkirst f2bc61b
Merge branch 'devel' into dbeaver/pro#11108-desktop-websocket-transport
vkirst 5dad707
dbeaver/pro#11108 Allow per-proxy RPC timeouts
vkirst 8252e2b
dbeaver/pro#11108 Centralize External DAM protocol and subscription c…
vkirst 2e9d4d2
dbeaver/pro#11108 Allow shared service secret for subscription callbacks
vkirst e028dfb
dbeaver/pro#11108 Merge messaging into renamed External DAM API module
vkirst aaf1ad7
dbeaver/pro#11108 Remove RPC topic subscription quota
vkirst 79dc9c8
dbeaver/pro#11108 Decouple RPC execution and use nonblocking HTTP
vkirst cb1a626
dbeaver/pro#11108 Preserve remote RPC exception diagnostics
vkirst 4974a44
dbeaver/pro#11108 Fix async External DAM cancellation and lint
vkirst f71b0da
dbeaver/pro#11108 Avoid Gson wording assumptions in RPC tests
vkirst File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
21 changes: 21 additions & 0 deletions
21
apis/com.dbeaver.datadam.external-dam.api/META-INF/MANIFEST.MF
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| Manifest-Version: 1.0 | ||
| Bundle-ManifestVersion: 2 | ||
| Bundle-Vendor: DBeaver Corp | ||
| Bundle-Name: DataDam External DAM API | ||
| Bundle-SymbolicName: com.dbeaver.datadam.external-dam.api | ||
| Bundle-Version: 0.0.17.qualifier | ||
| Bundle-RequiredExecutionEnvironment: JavaSE-21 | ||
| Export-Package: com.dbeaver.datadam.external.api, | ||
| com.dbeaver.datadam.external.api.client, | ||
| com.dbeaver.datadam.external.api.exception, | ||
| com.dbeaver.datadam.external.api.model, | ||
| com.dbeaver.datadam.external.api.requestreply, | ||
| com.dbeaver.datadam.external.api.rpc, | ||
| com.dbeaver.datadam.external.api.transport, | ||
| com.dbeaver.messaging.requestreply, | ||
| com.dbeaver.messaging.rpc | ||
| Bundle-ActivationPolicy: lazy | ||
| Require-Bundle: org.jkiss.utils, | ||
| com.dbeaver.rest.client;visibility:=reexport | ||
| Import-Package: com.google.gson | ||
| Automatic-Module-Name: com.dbeaver.datadam.external.dam.api | ||
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| source.. = src/main/java | ||
| output.. = target/classes/ | ||
| bin.includes = .,\ | ||
| META-INF/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <project xmlns="http://maven.apache.org/POM/4.0.0" | ||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| <modelVersion>4.0.0</modelVersion> | ||
| <parent> | ||
| <groupId>com.dbeaver.datadam</groupId> | ||
| <artifactId>api</artifactId> | ||
| <version>0.0.17-SNAPSHOT</version> | ||
| <relativePath>../pom.xml</relativePath> | ||
| </parent> | ||
|
|
||
| <artifactId>com.dbeaver.datadam.external-dam.api</artifactId> | ||
| <packaging>eclipse-plugin</packaging> | ||
| <name>DataDam External DAM API</name> | ||
|
|
||
| <dependencies> | ||
| <dependency> | ||
| <groupId>com.dbeaver.common</groupId> | ||
| <artifactId>org.jkiss.utils</artifactId> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>com.google.code.gson</groupId> | ||
| <artifactId>gson</artifactId> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>com.dbeaver.common</groupId> | ||
| <artifactId>com.dbeaver.rest.client</artifactId> | ||
| <version>${dbeaver.common.version}</version> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.junit.jupiter</groupId> | ||
| <artifactId>junit-jupiter-api</artifactId> | ||
| <scope>test</scope> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.junit.jupiter</groupId> | ||
| <artifactId>junit-jupiter-engine</artifactId> | ||
| <scope>test</scope> | ||
| </dependency> | ||
| </dependencies> | ||
|
|
||
| <build> | ||
| <sourceDirectory>${project.basedir}/src/main/java</sourceDirectory> | ||
| <testSourceDirectory>${project.basedir}/src/test/java</testSourceDirectory> | ||
|
|
||
| <plugins> | ||
| <plugin> | ||
| <groupId>org.apache.maven.plugins</groupId> | ||
| <artifactId>maven-surefire-plugin</artifactId> | ||
| <executions> | ||
| <execution> | ||
| <id>test</id> | ||
| <phase>test</phase> | ||
| <goals> | ||
| <goal>test</goal> | ||
| </goals> | ||
| </execution> | ||
| </executions> | ||
| </plugin> | ||
| </plugins> | ||
| </build> | ||
| </project> |
60 changes: 60 additions & 0 deletions
60
...dam.external-dam.api/src/main/java/com/dbeaver/datadam/external/api/DDExternalDamApi.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| /* | ||
| * DBeaver - Universal Database Manager | ||
| * Copyright (C) 2010-2026 DBeaver Corp | ||
| * | ||
| * All Rights Reserved. | ||
| * | ||
| * NOTICE: All information contained herein is, and remains | ||
| * the property of DBeaver Corp and its suppliers, if any. | ||
| * The intellectual and technical concepts contained | ||
| * herein are proprietary to DBeaver Corp and its suppliers | ||
| * and may be covered by U.S. and Foreign Patents, | ||
| * patents in process, and are protected by trade secret or copyright law. | ||
| * Dissemination of this information or reproduction of this material | ||
| * is strictly forbidden unless prior written permission is obtained | ||
| * from DBeaver Corp. | ||
| */ | ||
| package com.dbeaver.datadam.external.api; | ||
|
|
||
| import org.jkiss.code.Nullable; | ||
|
|
||
| import java.net.URI; | ||
|
|
||
| public final class DDExternalDamApi { | ||
| public static final String GRAPHQL_PATH = "/internal/graphql"; | ||
| public static final String CALLBACK_GRAPHQL_PATH = "/internal/external-dam/graphql"; | ||
| public static final String HEADER_SERVICE_TOKEN = "X-DD-Service-Token"; | ||
| public static final int MIN_SERVICE_SECRET_LENGTH = 32; | ||
| public static final int MAX_SERVICE_SECRET_LENGTH = 512; | ||
| public static final int MIN_SUBSCRIPTION_LEASE_SECONDS = 15; | ||
| public static final int MAX_SUBSCRIPTION_LEASE_SECONDS = 300; | ||
| private static final int MAX_SUBSCRIBER_ID_LENGTH = 256; | ||
| private static final int MAX_SERVICE_URL_LENGTH = 4096; | ||
|
|
||
| private DDExternalDamApi() { | ||
| } | ||
|
|
||
| public static boolean isValidSubscriberId(@Nullable String subscriberId) { | ||
| return subscriberId != null && !subscriberId.isBlank() && subscriberId.length() <= MAX_SUBSCRIBER_ID_LENGTH; | ||
| } | ||
|
|
||
| public static boolean isValidServiceSecret(@Nullable String secret) { | ||
| return secret != null && secret.length() >= MIN_SERVICE_SECRET_LENGTH && secret.length() <= MAX_SERVICE_SECRET_LENGTH | ||
| && secret.chars().allMatch(value -> value >= 33 && value <= 126); | ||
| } | ||
|
|
||
| public static boolean isValidServiceUrl(@Nullable String serviceUrl) { | ||
| if (serviceUrl == null || serviceUrl.length() > MAX_SERVICE_URL_LENGTH) { | ||
| return false; | ||
| } | ||
| try { | ||
| URI uri = URI.create(serviceUrl); | ||
| return uri.getHost() != null && uri.getUserInfo() == null && uri.getRawQuery() == null && uri.getRawFragment() == null | ||
| && ("https".equalsIgnoreCase(uri.getScheme()) || "http".equalsIgnoreCase(uri.getScheme())) | ||
| && (uri.getRawPath().isEmpty() || "/".equals(uri.getRawPath())) | ||
| && (uri.getPort() == -1 || uri.getPort() >= 1 && uri.getPort() <= 65535); | ||
| } catch (IllegalArgumentException e) { | ||
| return false; | ||
| } | ||
| } | ||
| } |
143 changes: 143 additions & 0 deletions
143
...xternal-dam.api/src/main/java/com/dbeaver/datadam/external/api/DDExternalDamProtocol.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,143 @@ | ||
| /* | ||
| * DBeaver - Universal Database Manager | ||
| * Copyright (C) 2010-2026 DBeaver Corp | ||
| * | ||
| * All Rights Reserved. | ||
| * | ||
| * NOTICE: All information contained herein is, and remains | ||
| * the property of DBeaver Corp and its suppliers, if any. | ||
| * The intellectual and technical concepts contained | ||
| * herein are proprietary to DBeaver Corp and its suppliers | ||
| * and may be covered by U.S. and Foreign Patents, | ||
| * patents in process, and are protected by trade secret or copyright law. | ||
| * Dissemination of this information or reproduction of this material | ||
| * is strictly forbidden unless prior written permission is obtained | ||
| * from DBeaver Corp. | ||
| */ | ||
| package com.dbeaver.datadam.external.api; | ||
|
|
||
| import com.dbeaver.datadam.external.api.model.DDHello; | ||
| import com.dbeaver.datadam.external.api.model.DDMessage; | ||
| import com.dbeaver.datadam.external.api.model.DDReady; | ||
| import com.google.gson.JsonElement; | ||
| import com.google.gson.JsonObject; | ||
| import com.google.gson.JsonParseException; | ||
| import com.google.gson.JsonParser; | ||
| import org.jkiss.code.NotNull; | ||
|
|
||
| public final class DDExternalDamProtocol { | ||
| public static final String WEBSOCKET_PATH = "/ws"; | ||
| public static final String TYPE_HELLO = "hello"; | ||
| public static final String TYPE_READY = "ready"; | ||
|
|
||
| private static final String FIELD_TOPIC = "topic"; | ||
| private static final String FIELD_TYPE = "type"; | ||
| private static final String FIELD_PAYLOAD = "payload"; | ||
| private static final String FIELD_DEPLOYMENT_ID = "deploymentId"; | ||
| private static final String FIELD_APPLICATION_RUN_ID = "applicationRunId"; | ||
| private static final String FIELD_PRODUCT = "product"; | ||
| private static final String FIELD_VERSION = "version"; | ||
| private static final String FIELD_CONNECTION_ID = "connectionId"; | ||
|
|
||
| private DDExternalDamProtocol() { | ||
| } | ||
|
|
||
| @NotNull | ||
| public static String encode(@NotNull DDMessage message) { | ||
| JsonObject envelope = envelope(message.type(), message.payload()); | ||
| envelope.addProperty(FIELD_TOPIC, message.topic()); | ||
| return envelope.toString(); | ||
| } | ||
|
|
||
| @NotNull | ||
| public static DDMessage decode(@NotNull String message) { | ||
| JsonObject envelope = parseEnvelope(message); | ||
| return new DDMessage( | ||
| readString(envelope, FIELD_TOPIC), | ||
| readString(envelope, FIELD_TYPE), | ||
| readPayload(envelope) | ||
| ); | ||
| } | ||
|
|
||
| @NotNull | ||
| public static String encodeHello(@NotNull DDHello hello) { | ||
| JsonObject payload = new JsonObject(); | ||
| payload.addProperty(FIELD_DEPLOYMENT_ID, hello.deploymentId()); | ||
| payload.addProperty(FIELD_APPLICATION_RUN_ID, hello.applicationRunId()); | ||
| payload.addProperty(FIELD_PRODUCT, hello.product()); | ||
| payload.addProperty(FIELD_VERSION, hello.version()); | ||
| return envelope(TYPE_HELLO, payload).toString(); | ||
| } | ||
|
|
||
| @NotNull | ||
| public static DDHello decodeHello(@NotNull String message) { | ||
| JsonObject payload = readControlPayload(message, TYPE_HELLO); | ||
| return new DDHello( | ||
| readString(payload, FIELD_DEPLOYMENT_ID), | ||
| readString(payload, FIELD_APPLICATION_RUN_ID), | ||
| readString(payload, FIELD_PRODUCT), | ||
| readString(payload, FIELD_VERSION) | ||
| ); | ||
| } | ||
|
|
||
| @NotNull | ||
| public static String encodeReady(@NotNull DDReady ready) { | ||
| JsonObject payload = new JsonObject(); | ||
| payload.addProperty(FIELD_CONNECTION_ID, ready.connectionId()); | ||
| return envelope(TYPE_READY, payload).toString(); | ||
| } | ||
|
|
||
| @NotNull | ||
| public static DDReady decodeReady(@NotNull String message) { | ||
| return new DDReady(readString(readControlPayload(message, TYPE_READY), FIELD_CONNECTION_ID)); | ||
| } | ||
|
|
||
| @NotNull | ||
| private static JsonObject envelope(@NotNull String type, @NotNull JsonObject payload) { | ||
| JsonObject envelope = new JsonObject(); | ||
| envelope.addProperty(FIELD_TYPE, type); | ||
| envelope.add(FIELD_PAYLOAD, payload); | ||
| return envelope; | ||
| } | ||
|
|
||
| @NotNull | ||
| private static JsonObject parseEnvelope(@NotNull String message) { | ||
| try { | ||
| JsonElement parsed = JsonParser.parseString(message); | ||
| if (!parsed.isJsonObject()) { | ||
| throw new IllegalArgumentException("Expected a message object"); | ||
| } | ||
| return parsed.getAsJsonObject(); | ||
| } catch (JsonParseException e) { | ||
| throw new IllegalArgumentException("Invalid message JSON", e); | ||
| } | ||
| } | ||
|
|
||
| @NotNull | ||
| private static JsonObject readControlPayload(@NotNull String message, @NotNull String expectedType) { | ||
| JsonObject envelope = parseEnvelope(message); | ||
| if (envelope.has(FIELD_TOPIC) || !expectedType.equals(readString(envelope, FIELD_TYPE))) { | ||
| throw new IllegalArgumentException("Expected a " + expectedType + " message"); | ||
| } | ||
| return readPayload(envelope); | ||
| } | ||
|
|
||
| @NotNull | ||
| private static JsonObject readPayload(@NotNull JsonObject envelope) { | ||
| JsonElement payload = envelope.get(FIELD_PAYLOAD); | ||
| if (payload == null || !payload.isJsonObject()) { | ||
| throw new IllegalArgumentException("Expected a payload object"); | ||
| } | ||
| return payload.getAsJsonObject(); | ||
| } | ||
|
|
||
| @NotNull | ||
| private static String readString(@NotNull JsonObject object, @NotNull String field) { | ||
| JsonElement value = object.get(field); | ||
| if (value == null || !value.isJsonPrimitive() || !value.getAsJsonPrimitive().isString() | ||
| || value.getAsString().isBlank()) { | ||
| throw new IllegalArgumentException("Expected a non-blank string for " + field); | ||
| } | ||
| return value.getAsString(); | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lets not use dashes in module names.
Maybe just com.dbeaver.datadam.dam.api ?