From 36d24e4ba0e989126c5cf4b6795857ad7cd16abf Mon Sep 17 00:00:00 2001 From: "Tobias Burdow [Kaleidox]" Date: Sat, 27 Jan 2024 13:22:54 +0100 Subject: [PATCH 1/7] Update Capitalization.java Revert "Update Capitalization.java" This reverts commit 9b48f4493b48d4a91a012915da315543bbe4cf37. Update DataNode.java --- src/main/java/org/comroid/api/data/seri/DataNode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/comroid/api/data/seri/DataNode.java b/src/main/java/org/comroid/api/data/seri/DataNode.java index d7c84f46..2776f877 100644 --- a/src/main/java/org/comroid/api/data/seri/DataNode.java +++ b/src/main/java/org/comroid/api/data/seri/DataNode.java @@ -331,7 +331,7 @@ public final Stream properties() { public String toString() { var str = String.valueOf(value); if (value instanceof String) - return "\"%s\"".formatted(str); + return "\"%s\"".formatted(str.replace("\"", "\\\"")); return str; } } From 4d3ff7f9be7ef0c35ff2c15731e7dee4d56b42c2 Mon Sep 17 00:00:00 2001 From: "Tobias Burdow [Kaleidox]" Date: Sat, 27 Jan 2024 14:12:25 +0100 Subject: [PATCH 2/7] Create InnerClass.java --- src/main/java/org/comroid/api/java/InnerClass.java | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 src/main/java/org/comroid/api/java/InnerClass.java diff --git a/src/main/java/org/comroid/api/java/InnerClass.java b/src/main/java/org/comroid/api/java/InnerClass.java new file mode 100644 index 00000000..8c73e490 --- /dev/null +++ b/src/main/java/org/comroid/api/java/InnerClass.java @@ -0,0 +1,5 @@ +package org.comroid.api.java; + +public interface InnerClass { + Of outer(); +} From 9301f30b973343bfae9f2e4dd743a42f99c28bcc Mon Sep 17 00:00:00 2001 From: "Tobias Burdow [Kaleidox]" Date: Sat, 27 Jan 2024 14:12:29 +0100 Subject: [PATCH 3/7] Create FileTransfer.java --- .../java/org/comroid/api/io/FileTransfer.java | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 src/main/java/org/comroid/api/io/FileTransfer.java diff --git a/src/main/java/org/comroid/api/io/FileTransfer.java b/src/main/java/org/comroid/api/io/FileTransfer.java new file mode 100644 index 00000000..27d183f2 --- /dev/null +++ b/src/main/java/org/comroid/api/io/FileTransfer.java @@ -0,0 +1,81 @@ +package org.comroid.api.io; + +import lombok.AccessLevel; +import lombok.Data; +import lombok.SneakyThrows; +import lombok.experimental.FieldDefaults; +import org.comroid.api.attr.Named; +import org.comroid.api.func.util.DelegateStream; +import org.jetbrains.annotations.Nullable; + +import java.io.*; +import java.net.MalformedURLException; +import java.net.URI; +import java.util.concurrent.CompletableFuture; + +@Data +@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) +public class FileTransfer { + URI source; + URI destination; + + public CompletableFuture execute() { + return CompletableFuture.supplyAsync(() -> { + try { + var inputMethod = Method.find(source.getScheme()); + var outputMethod = Method.find(destination.getScheme()); + var read = inputMethod.openRead(source); + var write = outputMethod.openWrite(destination); + DelegateStream.tryTransfer(read, write); + return this; + } catch (Throwable t) { + throw new RuntimeException("Failed to tranfer " + source + " to " + destination, t); + } + }); + } + + public enum Method implements Named { + http { + @Override + public InputStream openRead(URI uri) throws IOException { + return https.openRead(uri); + } + + @Override + public OutputStream openWrite(URI uri) { + return https.openWrite(uri); + } + }, + https { + @Override + public InputStream openRead(URI uri) throws IOException { + return uri.toURL().openStream(); + } + + @Override + public OutputStream openWrite(URI uri) { + throw new UnsupportedOperationException("Cannot write to HTTP/S connection"); + } + }, + file { + @Override + public InputStream openRead(URI uri) throws FileNotFoundException { + return new FileInputStream(uri.getPath()); + } + + @Override + public OutputStream openWrite(URI uri) throws FileNotFoundException { + return new FileOutputStream(uri.getPath()); + } + }; + + + public abstract InputStream openRead(URI uri) throws IOException; + + public abstract OutputStream openWrite(URI uri) throws IOException; + + public static Method find(@Nullable String scheme) { + return scheme == null ? https : valueOf(scheme); + } + } +} From 00f8553c12628f8712b0a3db20524c15c2ecdeb7 Mon Sep 17 00:00:00 2001 From: "Tobias Burdow [Kaleidox]" Date: Sat, 27 Jan 2024 14:16:17 +0100 Subject: [PATCH 4/7] Update FileTransfer.java --- src/main/java/org/comroid/api/io/FileTransfer.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/org/comroid/api/io/FileTransfer.java b/src/main/java/org/comroid/api/io/FileTransfer.java index 27d183f2..0722f96e 100644 --- a/src/main/java/org/comroid/api/io/FileTransfer.java +++ b/src/main/java/org/comroid/api/io/FileTransfer.java @@ -2,14 +2,12 @@ import lombok.AccessLevel; import lombok.Data; -import lombok.SneakyThrows; import lombok.experimental.FieldDefaults; import org.comroid.api.attr.Named; import org.comroid.api.func.util.DelegateStream; import org.jetbrains.annotations.Nullable; import java.io.*; -import java.net.MalformedURLException; import java.net.URI; import java.util.concurrent.CompletableFuture; From 9d5391b9ff78dad1e85679cc60a9a1d9ad309044 Mon Sep 17 00:00:00 2001 From: "Tobias Burdow [Kaleidox]" Date: Sat, 27 Jan 2024 14:16:19 +0100 Subject: [PATCH 5/7] Update REST.java --- src/main/java/org/comroid/api/net/REST.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/comroid/api/net/REST.java b/src/main/java/org/comroid/api/net/REST.java index 3b4b456b..6a47d7ef 100644 --- a/src/main/java/org/comroid/api/net/REST.java +++ b/src/main/java/org/comroid/api/net/REST.java @@ -10,6 +10,7 @@ import org.comroid.api.func.util.Cache; import org.comroid.api.info.Constraint; import org.comroid.api.data.seri.Jackson; +import org.comroid.api.io.FileTransfer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -101,7 +102,15 @@ public static Request request(Method method, String uri) { } public static Request request(Method method, String uri, @Nullable DataNode body) { - return Default.new Request(method, Polyfill.uri(uri), body, Default.serializer); + return Default.newRequest(method, uri, body); + } + + public Request newRequest(Method method, String uri) { + return newRequest(method, uri, null); + } + + public Request newRequest(Method method, String uri, @Nullable DataNode body) { + return new Request(method, Polyfill.uri(uri), body, Default.serializer); } @Value @@ -130,6 +139,10 @@ private Request(@NotNull Method method, @NotNull URI uri, @Nullable DataNode bod this.serializer = serializer; } + public FileTransfer asTransfer(URI destination) { + return new FileTransfer(uri, destination); + } + public CompletableFuture execute() { return executor.apply(this); } From 47c480ddde7ffba8567e1255773a42e780436f68 Mon Sep 17 00:00:00 2001 From: "Tobias Burdow [Kaleidox]" Date: Sat, 27 Jan 2024 15:18:46 +0100 Subject: [PATCH 6/7] add github class --- build.gradle | 1 + src/main/java/org/comroid/api/net/GitHub.java | 104 ++++++++++++++++++ .../java/org/comroid/test/api/GitHubTest.java | 39 +++++++ 3 files changed, 144 insertions(+) create mode 100644 src/main/java/org/comroid/api/net/GitHub.java create mode 100644 src/test/java/org/comroid/test/api/GitHubTest.java diff --git a/build.gradle b/build.gradle index 2ea99af3..22691d39 100644 --- a/build.gradle +++ b/build.gradle @@ -95,6 +95,7 @@ dependencies { testImplementation 'junit:junit:+' testImplementation 'org.easymock:easymock:+' + testImplementation 'com.fasterxml.jackson.core:jackson-databind:2.14.+' } apply from: 'gradle/publishing.gradle' diff --git a/src/main/java/org/comroid/api/net/GitHub.java b/src/main/java/org/comroid/api/net/GitHub.java new file mode 100644 index 00000000..b108c049 --- /dev/null +++ b/src/main/java/org/comroid/api/net/GitHub.java @@ -0,0 +1,104 @@ +package org.comroid.api.net; + +import lombok.Value; +import lombok.experimental.NonFinal; +import org.comroid.api.attr.Named; +import org.comroid.api.data.seri.DataNode; +import org.comroid.api.func.util.Streams; +import org.comroid.api.io.FileTransfer; +import org.comroid.api.java.InnerClass; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.net.URI; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +@Value +public class GitHub { + public static final GitHub Default = new GitHub(REST.Default, null); + + @NotNull REST rest; + @Nullable String token; + + public CompletableFuture> fetchReleases(String owner, String repo) { + var request = rest.newRequest(REST.Method.GET, + "https://api.github.com/repos/%s/%s/releases".formatted(owner, repo)); + if (token != null) + request.addHeader("Authorization", "Bearer " + token); + return request.execute() + .thenApply(REST.Response::validate2xxOK) + .thenApply(rsp -> rsp.getBody().asArray().stream() + .flatMap(Streams.cast(DataNode.class)) + .map(DataNode::asObject) + .map(obj -> obj.convert(Release.class)) + .toList()); + } + + @Value + @NonFinal + public static abstract class Entity implements Named { + long id; + URI htmlUrl; + } + + @Value + public class User extends Entity implements InnerClass { + String login; + + public User(long id, URI htmlUrl, String login) { + super(id, htmlUrl); + this.login = login; + } + + @Override + public String getPrimaryName() { + return login; + } + + @Override + public GitHub outer() { + return GitHub.this; + }} + + @Value + public class Release extends Entity implements InnerClass { + String name; + String tagName; + List assets; + + public Release(long id, URI htmlUrl, String name, String tagName, List assets) { + super(id, htmlUrl); + this.name = name; + this.tagName = tagName; + this.assets = assets; + } + + @Override + public GitHub outer() { + return GitHub.this; + } + + @Value + public class Asset extends Entity implements InnerClass { + String name; + URI browserDownloadUrl; + + public Asset(long id, URI htmlUrl, String name, URI browserDownloadUrl) { + super(id, htmlUrl); + this.name = name; + this.browserDownloadUrl = browserDownloadUrl; + } + + public FileTransfer download(File destination) { + return new FileTransfer(browserDownloadUrl, destination.toURI()); + } + + @Override + public Release outer() { + return Release.this; + } + } + } +} diff --git a/src/test/java/org/comroid/test/api/GitHubTest.java b/src/test/java/org/comroid/test/api/GitHubTest.java new file mode 100644 index 00000000..50a131bc --- /dev/null +++ b/src/test/java/org/comroid/test/api/GitHubTest.java @@ -0,0 +1,39 @@ +package org.comroid.test.api; + +import org.comroid.api.net.GitHub; +import org.comroid.api.net.MD5; +import org.comroid.api.net.REST; +import org.junit.Assert; +import org.junit.Test; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +public class GitHubTest { + @Test(timeout = 5000) + public void testDownload() throws IOException, ExecutionException, InterruptedException { + var releases = GitHub.Default.fetchReleases("comroid-git", "clmath").join(); + var file = File.createTempFile("japi-test-github-dl-clmath", ".zip"); + file.deleteOnExit(); + + var tasks = releases.stream() + .flatMap(release -> release.getAssets().stream()) + .findAny() + .map(asset -> new CompletableFuture[]{ + REST.get("https://api.comroid.org/md5?url=" + asset.getBrowserDownloadUrl()) + .thenApply(response -> response.getBody().asString()), + asset.download(file).execute() + }) + .orElseThrow(); + + CompletableFuture.allOf(tasks).join(); + + var original = tasks[0].get().toString(); + var output = MD5.calculate(new FileInputStream(file)); + + Assert.assertEquals("invalid md5 of downloaded file", original, output); + } +} From 4954dc2df4f955b007895012eecd2c880d02e8cd Mon Sep 17 00:00:00 2001 From: "Tobias Burdow [Kaleidox]" Date: Sat, 27 Jan 2024 15:18:53 +0100 Subject: [PATCH 7/7] fixes and logging --- .../java/org/comroid/api/io/FileTransfer.java | 36 ++++++++++--------- .../java/org/comroid/api/java/Activator.java | 3 +- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/comroid/api/io/FileTransfer.java b/src/main/java/org/comroid/api/io/FileTransfer.java index 0722f96e..feffea69 100644 --- a/src/main/java/org/comroid/api/io/FileTransfer.java +++ b/src/main/java/org/comroid/api/io/FileTransfer.java @@ -1,35 +1,39 @@ package org.comroid.api.io; import lombok.AccessLevel; -import lombok.Data; +import lombok.Value; import lombok.experimental.FieldDefaults; import org.comroid.api.attr.Named; import org.comroid.api.func.util.DelegateStream; +import org.comroid.api.func.util.OnDemand; import org.jetbrains.annotations.Nullable; import java.io.*; import java.net.URI; import java.util.concurrent.CompletableFuture; -@Data +@Value @FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) public class FileTransfer { URI source; URI destination; + OnDemand exection = new OnDemand<>(this::exec); public CompletableFuture execute() { - return CompletableFuture.supplyAsync(() -> { - try { - var inputMethod = Method.find(source.getScheme()); - var outputMethod = Method.find(destination.getScheme()); - var read = inputMethod.openRead(source); - var write = outputMethod.openWrite(destination); - DelegateStream.tryTransfer(read, write); - return this; - } catch (Throwable t) { - throw new RuntimeException("Failed to tranfer " + source + " to " + destination, t); - } - }); + return exection.get(); + } + + private FileTransfer exec() { + try { + var inputMethod = Method.find(source.getScheme()); + var outputMethod = Method.find(destination.getScheme()); + var read = inputMethod.openRead(source); + var write = outputMethod.openWrite(destination); + DelegateStream.tryTransfer(read, write); + return this; + } catch (Throwable t) { + throw new RuntimeException("Failed to tranfer " + source + " to " + destination, t); + } } public enum Method implements Named { @@ -40,7 +44,7 @@ public InputStream openRead(URI uri) throws IOException { } @Override - public OutputStream openWrite(URI uri) { + public OutputStream openWrite(URI uri) throws IOException { return https.openWrite(uri); } }, @@ -51,7 +55,7 @@ public InputStream openRead(URI uri) throws IOException { } @Override - public OutputStream openWrite(URI uri) { + public OutputStream openWrite(URI uri) throws IOException { throw new UnsupportedOperationException("Cannot write to HTTP/S connection"); } }, diff --git a/src/main/java/org/comroid/api/java/Activator.java b/src/main/java/org/comroid/api/java/Activator.java index dd3ecfe5..a4c7d812 100644 --- a/src/main/java/org/comroid/api/java/Activator.java +++ b/src/main/java/org/comroid/api/java/Activator.java @@ -70,6 +70,7 @@ public T createInstance(DataNode data) { return it; }) - .orElseThrow(() -> new NoSuchElementException("No suitable constructor was found for "+struct)); + .orElseThrow(() -> new NoSuchElementException("No suitable constructor was found for "+struct + +"; available properties:\n\t- "+String.join("\n\t- ", obj.keySet()))); } }