diff --git a/app/src/main/java/io/xpipe/app/beacon/AppBeaconServer.java b/app/src/main/java/io/xpipe/app/beacon/AppBeaconServer.java index e66f20bb..87203792 100644 --- a/app/src/main/java/io/xpipe/app/beacon/AppBeaconServer.java +++ b/app/src/main/java/io/xpipe/app/beacon/AppBeaconServer.java @@ -19,6 +19,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.*; import java.util.concurrent.Executors; +import java.util.regex.Pattern; public class AppBeaconServer { @@ -161,8 +162,17 @@ public class AppBeaconServer { private void handleCatchAll(HttpExchange exchange) throws IOException { if (notFoundHtml == null) { AppResources.with(AppResources.XPIPE_MODULE, "misc/api.md", file -> { - notFoundHtml = MarkdownHelper.toHtml( - Files.readString(file), + var md = Files.readString(file); + md = md.replaceAll(Pattern.quote( """ + > 400 Response + + ```json + { + "message": "string" + } + ``` + """), ""); + notFoundHtml = MarkdownHelper.toHtml(md, head -> { return head + "\n" + "" + "\n" + "" diff --git a/app/src/main/java/io/xpipe/app/beacon/impl/ConnectionInfoExchangeImpl.java b/app/src/main/java/io/xpipe/app/beacon/impl/ConnectionInfoExchangeImpl.java index 2c08b75c..a1dc77f4 100644 --- a/app/src/main/java/io/xpipe/app/beacon/impl/ConnectionInfoExchangeImpl.java +++ b/app/src/main/java/io/xpipe/app/beacon/impl/ConnectionInfoExchangeImpl.java @@ -4,16 +4,31 @@ import com.sun.net.httpserver.HttpExchange; import io.xpipe.app.storage.DataStorage; import io.xpipe.beacon.BeaconClientException; import io.xpipe.beacon.api.ConnectionInfoExchange; +import io.xpipe.core.store.StorePath; + +import java.util.ArrayList; +import java.util.UUID; public class ConnectionInfoExchangeImpl extends ConnectionInfoExchange { @Override public Object handle(HttpExchange exchange, Request msg) throws BeaconClientException { - var e = DataStorage.get() - .getStoreEntryIfPresent(msg.getConnection()) - .orElseThrow(() -> new BeaconClientException("Unknown connection")); + var list = new ArrayList(); + for (UUID uuid : msg.getConnections()) { + var e = DataStorage.get().getStoreEntryIfPresent(uuid).orElseThrow(() -> new BeaconClientException("Unknown connection: " + uuid)); - return Response.builder().lastModified(e.getLastModified()).lastUsed(e.getLastUsed()).connection(e.getCategoryUuid()).category(DataStorage.get() - .getStorePath(DataStorage.get().getStoreCategoryIfPresent(e.getCategoryUuid()).orElseThrow())).name(DataStorage.get().getStorePath(e)).rawData(e.getStore()).usageCategory(e.getProvider().getUsageCategory()).type(e.getProvider().getId()).build(); + var names = DataStorage.get() + .getStorePath(DataStorage.get() + .getStoreCategoryIfPresent(e.getCategoryUuid()) + .orElseThrow()) + .getNames(); + var cat = new StorePath(names.subList(1, names.size())); + + var apply = InfoResponse.builder().lastModified(e.getLastModified()).lastUsed(e.getLastUsed()).connection(e.getCategoryUuid()).category(cat).name( + DataStorage.get().getStorePath(e)).rawData(e.getStore()).usageCategory(e.getProvider().getUsageCategory()).type( + e.getProvider().getId()).state(e.getStorePersistentState()).build(); + list.add(apply); + } + return Response.builder().infos(list).build(); } } diff --git a/app/src/main/java/io/xpipe/app/beacon/impl/ConnectionQueryExchangeImpl.java b/app/src/main/java/io/xpipe/app/beacon/impl/ConnectionQueryExchangeImpl.java index 109a9878..a1277391 100644 --- a/app/src/main/java/io/xpipe/app/beacon/impl/ConnectionQueryExchangeImpl.java +++ b/app/src/main/java/io/xpipe/app/beacon/impl/ConnectionQueryExchangeImpl.java @@ -1,11 +1,9 @@ package io.xpipe.app.beacon.impl; +import com.sun.net.httpserver.HttpExchange; import io.xpipe.app.storage.DataStorage; import io.xpipe.app.storage.DataStoreEntry; import io.xpipe.beacon.api.ConnectionQueryExchange; -import io.xpipe.core.store.StorePath; - -import com.sun.net.httpserver.HttpExchange; import java.util.ArrayList; import java.util.List; @@ -51,23 +49,7 @@ public class ConnectionQueryExchangeImpl extends ConnectionQueryExchange { found.add(storeEntry); } - var mapped = new ArrayList(); - for (DataStoreEntry e : found) { - var names = DataStorage.get() - .getStorePath(DataStorage.get() - .getStoreCategoryIfPresent(e.getCategoryUuid()) - .orElseThrow()) - .getNames(); - var cat = new StorePath(names.subList(1, names.size())); - var obj = ConnectionQueryExchange.QueryResponse.builder() - .connection(e.getUuid()) - .category(cat) - .name(DataStorage.get().getStorePath(e)) - .type(e.getProvider().getId()) - .build(); - mapped.add(obj); - } - return Response.builder().found(mapped).build(); + return Response.builder().found(found.stream().map(entry -> entry.getUuid()).toList()).build(); } private String toRegex(String pattern) { diff --git a/app/src/main/resources/io/xpipe/app/resources/misc/api.md b/app/src/main/resources/io/xpipe/app/resources/misc/api.md index 4660fb53..278fc122 100644 --- a/app/src/main/resources/io/xpipe/app/resources/misc/api.md +++ b/app/src/main/resources/io/xpipe/app/resources/misc/api.md @@ -279,29 +279,7 @@ All matching is case insensitive. ```json { "found": [ - { - "connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b", - "category": [ - "default" - ], - "name": [ - "local machine" - ], - "type": "local" - }, - { - "connection": "e1462ddc-9beb-484c-bd91-bb666027e300", - "category": [ - "default", - "category 1" - ], - "name": [ - "ssh system", - "shell environments", - "bash" - ], - "type": "shellEnvironment" - } + "f0ec68aa-63f5-405c-b178-9a4454556d6b" ] } ``` @@ -459,7 +437,9 @@ Queries detailed information about a connection. ```json { - "connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b" + "connections": [ + "f0ec68aa-63f5-405c-b178-9a4454556d6b" + ] } ``` @@ -471,22 +451,35 @@ Queries detailed information about a connection. > Example responses -> 200 Response +> The query was successful. The body contains the detailed connection information. ```json { - "connection": "string", - "category": [ - "string" - ], - "name": [ - "string" - ], - "type": "string", - "rawData": {}, - "usageCategory": "shell", - "lastModified": "string", - "lastUsed": "string" + "infos": [ + { + "connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b", + "category": [ + "default" + ], + "name": [ + "local machine" + ], + "type": "local", + "rawData": {}, + "usageCategory": "shell", + "lastUsed": "2024-05-31T11:53:02.408504600Z", + "lastModified": "2024-06-23T21:15:25.608097Z", + "state": {} + } + ] +} +``` + +> 400 Response + +```json +{ + "message": "string" } ``` @@ -512,7 +505,9 @@ bearerAuth ```javascript const inputBody = '{ - "connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b" + "connections": [ + "f0ec68aa-63f5-405c-b178-9a4454556d6b" + ] }'; const headers = { 'Content-Type':'application/json', @@ -544,7 +539,9 @@ headers = { data = """ { - "connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b" + "connections": [ + "f0ec68aa-63f5-405c-b178-9a4454556d6b" + ] } """ r = requests.post('http://localhost:21723/connection/info', headers = headers, data = data) @@ -564,7 +561,9 @@ var request = HttpRequest .header("Authorization", "Bearer {access-token}") .POST(HttpRequest.BodyPublishers.ofString(""" { - "connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b" + "connections": [ + "f0ec68aa-63f5-405c-b178-9a4454556d6b" + ] } """)) .build(); @@ -607,7 +606,9 @@ curl -X POST http://localhost:21723/connection/info \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ -H 'Authorization: Bearer {access-token}' \ --data ' { - "connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b" + "connections": [ + "f0ec68aa-63f5-405c-b178-9a4454556d6b" + ] } ' @@ -2011,16 +2012,7 @@ curl -X POST http://localhost:21723/fs/script \ ```json { "found": [ - { - "connection": "string", - "category": [ - "string" - ], - "name": [ - "string" - ], - "type": "string" - } + "string" ] } @@ -2030,11 +2022,7 @@ curl -X POST http://localhost:21723/fs/script \ |Name|Type|Required|Restrictions|Description| |---|---|---|---|---| -|found|[object]|true|none|The found connections| -|» connection|string|true|none|The unique id of the connection| -|» category|[string]|true|none|The full category path as an array| -|» name|[string]|true|none|The full connection name path as an array| -|» type|string|true|none|The type identifier of the connection| +|found|[string]|true|none|The found connections|

ConnectionInfoRequest

@@ -2045,7 +2033,9 @@ curl -X POST http://localhost:21723/fs/script \ ```json { - "connection": "string" + "connections": [ + "string" + ] } ``` @@ -2054,7 +2044,7 @@ curl -X POST http://localhost:21723/fs/script \ |Name|Type|Required|Restrictions|Description| |---|---|---|---|---| -|connection|string|true|none|The unique id of the connection| +|connections|[string]|true|none|The connections|

ConnectionInfoResponse

@@ -2064,20 +2054,23 @@ curl -X POST http://localhost:21723/fs/script \ ```json -{ - "connection": "string", - "category": [ - "string" - ], - "name": [ - "string" - ], - "type": "string", - "rawData": {}, - "usageCategory": "shell", - "lastModified": "string", - "lastUsed": "string" -} +[ + { + "connection": "string", + "category": [ + "string" + ], + "name": [ + "string" + ], + "type": "string", + "rawData": {}, + "usageCategory": "shell", + "lastModified": "string", + "lastUsed": "string", + "state": {} + } +] ``` @@ -2091,8 +2084,9 @@ curl -X POST http://localhost:21723/fs/script \ |type|string|true|none|The type identifier of the connection| |rawData|object|true|none|The raw internal configuration data for the connection. The schema for these is internal and should not be relied upon.| |usageCategory|string|true|none|The category of how this connection can be used.| -|lastModified|string|true|none|The timestamp of when the connection configuration was last modified in ISO 8601.| -|lastUsed|string|true|none|The timestamp of when the connection was last launched in ISO 8601.| +|lastModified|string|true|none|The timestamp of when the connection configuration was last modified in ISO 8601| +|lastUsed|string|true|none|The timestamp of when the connection was last launched in ISO 8601| +|state|object|true|none|The internal persistent state information about the connection| #### Enumerated Values diff --git a/app/src/main/resources/io/xpipe/app/resources/style/store-entry-comp.css b/app/src/main/resources/io/xpipe/app/resources/style/store-entry-comp.css index 933c5783..02527409 100644 --- a/app/src/main/resources/io/xpipe/app/resources/style/store-entry-comp.css +++ b/app/src/main/resources/io/xpipe/app/resources/style/store-entry-comp.css @@ -1,9 +1,10 @@ .store-list-comp.scroll-pane > .viewport .list-box-content { -fx-spacing: 8; + -fx-padding: 8 0 0 0; } .store-list-comp.scroll-pane { - -fx-padding: 8 2 0 6; + -fx-padding: 0 2 0 6; } .store-list-comp.scroll-pane .scroll-bar:vertical { diff --git a/beacon/src/main/java/io/xpipe/beacon/api/ConnectionInfoExchange.java b/beacon/src/main/java/io/xpipe/beacon/api/ConnectionInfoExchange.java index 1e0d6d1c..653e08b7 100644 --- a/beacon/src/main/java/io/xpipe/beacon/api/ConnectionInfoExchange.java +++ b/beacon/src/main/java/io/xpipe/beacon/api/ConnectionInfoExchange.java @@ -8,6 +8,7 @@ import lombok.Value; import lombok.extern.jackson.Jacksonized; import java.time.Instant; +import java.util.List; import java.util.UUID; public class ConnectionInfoExchange extends BeaconInterface { @@ -22,13 +23,22 @@ public class ConnectionInfoExchange extends BeaconInterface connections; } @Jacksonized @Builder @Value public static class Response { + @NonNull + List<@NonNull InfoResponse> infos; + } + + + @Jacksonized + @Builder + @Value + public static class InfoResponse { @NonNull UUID connection; @@ -52,5 +62,8 @@ public class ConnectionInfoExchange extends BeaconInterface found; - } - - @Jacksonized - @Builder - @Value - public static class QueryResponse { - @NonNull - UUID connection; - - @NonNull - StorePath category; - - @NonNull - StorePath name; - - @NonNull - String type; + List<@NonNull UUID> found; } } diff --git a/openapi.yaml b/openapi.yaml index bb59612c..cfa6ea8b 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -104,10 +104,7 @@ paths: examples: standard: summary: Matched connections - value: { "found": [ { "connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b", "category": ["default"] , - "name": ["local machine"], "type": "local" }, - { "connection": "e1462ddc-9beb-484c-bd91-bb666027e300", "category": ["default", "category 1"], - "name": ["ssh system", "shell environments", "bash"], "type": "shellEnvironment" } ] } + value: { "found": [ "f0ec68aa-63f5-405c-b178-9a4454556d6b"] } '400': $ref: '#/components/responses/BadRequest' '401': @@ -133,7 +130,7 @@ paths: examples: simple: summary: Standard - value: { "connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b" } + value: { "connections": ["f0ec68aa-63f5-405c-b178-9a4454556d6b"] } responses: '200': description: The query was successful. The body contains the detailed connection information. @@ -141,6 +138,13 @@ paths: application/json: schema: $ref: '#/components/schemas/ConnectionInfoResponse' + examples: + standard: + summary: Connection information + value: { "infos": [ { "connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b", "category": ["default"] , + "name": ["local machine"], "type": "local", "rawData" : {}, "usageCategory" : "shell", + "lastUsed" : "2024-05-31T11:53:02.408504600Z", "lastModified" : "2024-06-23T21:15:25.608097Z", + "state": {} } ] } '400': $ref: '#/components/responses/BadRequest' '401': @@ -526,91 +530,78 @@ components: type: array description: The found connections items: - type: object - properties: - connection: - type: string - description: The unique id of the connection - category: - type: array - description: The full category path as an array - items: - type: string - description: Individual category name - name: - type: array - description: The full connection name path as an array - items: - type: string - description: Individual connection name - type: - type: string - description: The type identifier of the connection - required: - - connection - - category - - name - - type + type: string + description: The connection uuid required: - found ConnectionInfoRequest: type: object properties: - connection: - type: string - description: The unique id of the connection + connections: + type: array + description: The connections + items: + type: string + description: The unique id of the connection required: - - connection + - connections ConnectionInfoResponse: - type: object - properties: - connection: - type: string - description: The unique id of the connection - category: - type: array - description: The full category path as an array - items: + type: array + items: + type: object + description: The array of information for each connection + properties: + connection: type: string - description: Individual category name - name: - type: array - description: The full connection name path as an array - items: + description: The unique id of the connection + category: + type: array + description: The full category path as an array + items: + type: string + description: Individual category name + name: + type: array + description: The full connection name path as an array + items: + type: string + description: Individual connection name + type: type: string - description: Individual connection name - type: - type: string - description: The type identifier of the connection - rawData: - type: object - description: The raw internal configuration data for the connection. The schema for these is internal and should not be relied upon. - usageCategory: - type: string - description: The category of how this connection can be used. - enum: - - shell - - tunnel - - script - - database - - command - - desktop - - group - lastModified: - type: string - description: The timestamp of when the connection configuration was last modified in ISO 8601. - lastUsed: - type: string - description: The timestamp of when the connection was last launched in ISO 8601. - required: - - connection - - category - - name - - type - - rawData - - usageCategory - - lastUsed - - lastModified + description: The type identifier of the connection + rawData: + type: object + description: The raw internal configuration data for the connection. The schema for these is internal and should not be relied upon. + usageCategory: + type: string + description: The category of how this connection can be used. + enum: + - shell + - tunnel + - script + - database + - command + - desktop + - group + lastModified: + type: string + description: The timestamp of when the connection configuration was last modified in ISO 8601 + lastUsed: + type: string + description: The timestamp of when the connection was last launched in ISO 8601 + state: + type: object + description: The internal persistent state information about the connection + required: + - connection + - category + - name + - type + - rawData + - usageCategory + - lastUsed + - lastModified + - state HandshakeRequest: type: object properties: