Large refactor

This commit is contained in:
Christopher Schnick 2022-06-18 00:29:41 +02:00
parent 9440037f03
commit 3cc527dfa3
76 changed files with 359 additions and 387 deletions

View file

@ -1,5 +1,6 @@
## X-Pipe Java
The fundamental components of the [X-Pipe project]().
This repository contains the following four modules:
- Core - Shared core classes of the Java API and the X-Pipe daemon implementation
@ -8,6 +9,18 @@ This repository contains the following four modules:
and the client applications, for example the various programming language APIs and the CLI
- Extension - An API to create all different kinds of extensions for the X-Pipe platform
## Installation / Usage
The *core* and *extension* libraries are used in X-Pipe extension development.
For setup instructions, see the [X-Pipe extension development]() section.
The *beacon* library handles all communication and serves as a
reference when implementing an API or program that communicates with the X-Pipe daemon.
The *api* library serves as a reference implementation for other potential X-Pipe APIs
and is also used to enable your Java program to communicate with X-Pipe.
For setup instructions, see the [X-Pipe Java API Usage]() section.
## Development
All X-Pipe components target [JDK 17](https://openjdk.java.net/projects/jdk/17/) and make full use of the Java Module System (JPMS).

View file

@ -1,3 +1,18 @@
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.xpipe/api/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.xpipe/api)
[![javadoc](https://javadoc.io/badge2/io.xpipe/api/javadoc.svg)](https://javadoc.io/doc/io.xpipe/api)
[![Build Status](https://github.com/xpipe-io/xpipe_java/actions/workflows/api-build.yml/badge.svg)](https://github.com/xpipe-io/xpipe_java/actions/workflows/api-build.yml)
## X-Pipe Java API
The X-Pipe API for Java allows you to use most of the X-Pipe functionality from Java applications:
- Create data stores and data sources
- Query and work with the contents of data sources
- Write and append to data sources
## Setup
Either install the [dependency](https://maven-badges.herokuapp.com/maven-central/io.xpipe/api) from Maven Central
using your favourite build tool or alternatively download the `xpipe-api.jar`, `xpipe-core.jar`, and `xpipe-beacon.jar`
from the [releases page](https://github.com/xpipe-io/xpipe_java/releases/latest) and add them to the classpath.

View file

@ -152,7 +152,7 @@ public interface DataSource {
DataSourceType getType();
DataSourceConfigInstance getConfig();
DataSourceConfig getConfig();
/**
* Attempts to cast this object to a {@link DataTable}.

View file

@ -0,0 +1,32 @@
package io.xpipe.api;
import java.util.Map;
/**
* Represents the current configuration of a data source.
*/
public final class DataSourceConfig {
/**
* The data source provider id.
*/
private final String provider;
/**
* The set configuration parameters.
*/
private final Map<String, String> configInstance;
public DataSourceConfig(String provider, Map<String, String> configInstance) {
this.provider = provider;
this.configInstance = configInstance;
}
public String getProvider() {
return provider;
}
public Map<String, String> getConfigInstance() {
return configInstance;
}
}

View file

@ -1,6 +1,7 @@
package io.xpipe.api.impl;
import io.xpipe.api.DataRaw;
import io.xpipe.api.DataSourceConfig;
import io.xpipe.core.source.*;
import java.io.InputStream;
@ -9,7 +10,7 @@ public class DataRawImpl extends DataSourceImpl implements DataRaw {
private final DataSourceInfo.Raw info;
public DataRawImpl(DataSourceId sourceId, DataSourceConfigInstance sourceConfig, DataSourceInfo.Raw info) {
public DataRawImpl(DataSourceId sourceId, DataSourceConfig sourceConfig, DataSourceInfo.Raw info) {
super(sourceId, sourceConfig);
this.info = info;
}

View file

@ -1,12 +1,11 @@
package io.xpipe.api.impl;
import io.xpipe.api.DataSource;
import io.xpipe.api.DataSourceConfig;
import io.xpipe.api.connector.XPipeConnection;
import io.xpipe.beacon.exchange.StoreStreamExchange;
import io.xpipe.beacon.exchange.QueryDataSourceExchange;
import io.xpipe.beacon.exchange.ReadExecuteExchange;
import io.xpipe.beacon.exchange.ReadPreparationExchange;
import io.xpipe.core.source.DataSourceConfigInstance;
import io.xpipe.beacon.exchange.StoreStreamExchange;
import io.xpipe.core.source.DataSourceId;
import io.xpipe.core.source.DataSourceReference;
@ -19,22 +18,23 @@ public abstract class DataSourceImpl implements DataSource {
return XPipeConnection.execute(con -> {
var req = QueryDataSourceExchange.Request.builder().ref(ds).build();
QueryDataSourceExchange.Response res = con.performSimpleExchange(req);
var config = new DataSourceConfig(res.getProvider(), res.getConfig());
switch (res.getInfo().getType()) {
case TABLE -> {
var data = res.getInfo().asTable();
return new DataTableImpl(res.getId(), res.getConfig(), data);
return new DataTableImpl(res.getId(), config, data);
}
case STRUCTURE -> {
var info = res.getInfo().asStructure();
return new DataStructureImpl(res.getId(), res.getConfig(), info);
return new DataStructureImpl(res.getId(), config, info);
}
case TEXT -> {
var info = res.getInfo().asText();
return new DataTextImpl(res.getId(), res.getConfig(), info);
return new DataTextImpl(res.getId(), config, info);
}
case RAW -> {
var info = res.getInfo().asRaw();
return new DataRawImpl(res.getId(), res.getConfig(), info);
return new DataRawImpl(res.getId(), config, info);
}
}
throw new AssertionError();
@ -60,20 +60,21 @@ public abstract class DataSourceImpl implements DataSource {
});
var configInstance = startRes.getConfig();
configInstance.getConfigInstance().getCurrentValues().putAll(config);
var endReq = ReadExecuteExchange.Request.builder()
.target(id).dataStore(store).config(configInstance).build();
XPipeConnection.execute(con -> {
con.performSimpleExchange(endReq);
});
//TODO
// configInstance.getConfigInstance().getCurrentValues().putAll(config);
// var endReq = ReadExecuteExchange.Request.builder()
// .target(id).dataStore(store).config(configInstance).build();
// XPipeConnection.execute(con -> {
// con.performSimpleExchange(endReq);
// });
var ref = id != null ? DataSourceReference.id(id) : DataSourceReference.latest();
return get(ref);
}
private final DataSourceId sourceId;
private final DataSourceConfigInstance config;
private final DataSourceConfig config;
public DataSourceImpl(DataSourceId sourceId, DataSourceConfigInstance config) {
public DataSourceImpl(DataSourceId sourceId, DataSourceConfig config) {
this.sourceId = sourceId;
this.config = config;
}
@ -84,7 +85,7 @@ public abstract class DataSourceImpl implements DataSource {
}
@Override
public DataSourceConfigInstance getConfig() {
public DataSourceConfig getConfig() {
return config;
}
}

View file

@ -1,5 +1,6 @@
package io.xpipe.api.impl;
import io.xpipe.api.DataSourceConfig;
import io.xpipe.api.DataStructure;
import io.xpipe.core.data.node.DataStructureNode;
import io.xpipe.core.source.*;
@ -8,7 +9,7 @@ public class DataStructureImpl extends DataSourceImpl implements DataStructure {
private final DataSourceInfo.Structure info;
public DataStructureImpl(DataSourceId sourceId, DataSourceConfigInstance sourceConfig, DataSourceInfo.Structure info) {
public DataStructureImpl(DataSourceId sourceId, DataSourceConfig sourceConfig, DataSourceInfo.Structure info) {
super(sourceId, sourceConfig);
this.info = info;
}

View file

@ -5,14 +5,13 @@ import io.xpipe.api.DataTable;
import io.xpipe.api.DataTableAccumulator;
import io.xpipe.api.connector.XPipeConnection;
import io.xpipe.api.util.TypeDescriptor;
import io.xpipe.beacon.exchange.StoreStreamExchange;
import io.xpipe.beacon.exchange.ReadExecuteExchange;
import io.xpipe.beacon.exchange.StoreStreamExchange;
import io.xpipe.core.data.node.DataStructureNode;
import io.xpipe.core.data.node.DataStructureNodeAcceptor;
import io.xpipe.core.data.node.TupleNode;
import io.xpipe.core.data.type.TupleType;
import io.xpipe.core.data.typed.TypedDataStreamWriter;
import io.xpipe.core.source.DataSourceConfigInstance;
import io.xpipe.core.source.DataSourceId;
import io.xpipe.core.source.DataSourceReference;
@ -40,7 +39,7 @@ public class DataTableAccumulatorImpl implements DataTableAccumulator {
connection.close();
var req = ReadExecuteExchange.Request.builder()
.target(id).dataStore(res.getStore()).config(DataSourceConfigInstance.xpbt()).build();
.target(id).dataStore(res.getStore()).build();
XPipeConnection.execute(con -> {
con.performSimpleExchange(req);
});

View file

@ -1,5 +1,6 @@
package io.xpipe.api.impl;
import io.xpipe.api.DataSourceConfig;
import io.xpipe.api.DataTable;
import io.xpipe.api.connector.XPipeConnection;
import io.xpipe.beacon.BeaconConnection;
@ -24,7 +25,7 @@ public class DataTableImpl extends DataSourceImpl implements DataTable {
private final DataSourceInfo.Table info;
DataTableImpl(DataSourceId id, DataSourceConfigInstance sourceConfig, DataSourceInfo.Table info) {
DataTableImpl(DataSourceId id, DataSourceConfig sourceConfig, DataSourceInfo.Table info) {
super(id, sourceConfig);
this.info = info;
}

View file

@ -1,5 +1,6 @@
package io.xpipe.api.impl;
import io.xpipe.api.DataSourceConfig;
import io.xpipe.api.DataText;
import io.xpipe.core.source.*;
@ -10,7 +11,7 @@ public class DataTextImpl extends DataSourceImpl implements DataText {
private final DataSourceInfo.Text info;
public DataTextImpl(DataSourceId sourceId, DataSourceConfigInstance sourceConfig, DataSourceInfo.Text info) {
public DataTextImpl(DataSourceId sourceId, DataSourceConfig sourceConfig, DataSourceInfo.Text info) {
super(sourceId, sourceConfig);
this.info = info;
}

View file

@ -7,10 +7,10 @@
The X-Pipe beacon component is responsible for handling all communications between the X-Pipe daemon
and the various programming language APIs and the CLI. It provides an API that supports all kinds
of different operations.
The underlying inter-process communication is realized through TCP sockets on port `21721`.
The underlying inter-process communication is realized through TCP sockets on default port `21721`.
The data structures and exchange protocols are specified in the `io.xpipe.beacon.exchange` package.
Every exchange is initiated from the outside by sending a request message to the daemon.
Every exchange is initiated from the outside by sending a request message to the X-Pipe daemon.
The daemon then always sends a response message.
The header information of a message is formatted in the json format.
@ -23,7 +23,7 @@ Each segment is preceded by four bytes that specify the length of the next segme
In case the next segment has a length of less than `65536` bytes, we know that the end of the body has been reached.
This way the socket communication can handle payloads of unknown length.
### Configuration
## Configuration
The default port used by the beacon implementation of the X-Pipe daemon and APIs is `21721`.
It can be changed by passing the property `io.xpipe.beacon.port=<port>` to both the daemon and APIs.
@ -32,6 +32,7 @@ The beacon API also supports launching the daemon automatically in case it is no
By default, it launches the daemon of the local X-Pipe installation.
It is possible to pass a custom launch command with the property `io.xpipe.beacon.exec=<cmd>`.
This allows for a custom launch behaviour in a testing/development environment.
Note that the `<cmd>` value has to be a single property string, which can be prone to formatting errors
By passing the property `io.xpipe.beacon.debugOutput=true`, it is possible to print debug information
about the underlying communications.

View file

@ -8,8 +8,6 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
import io.xpipe.beacon.exchange.MessageExchanges;
import io.xpipe.beacon.exchange.data.ClientErrorMessage;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.beacon.exchange.data.ServerErrorMessage;
import io.xpipe.core.util.JacksonHelper;
@ -32,12 +30,6 @@ public class BeaconClient implements AutoCloseable {
void accept(T var1, U var2) throws E;
}
@FunctionalInterface
public interface FailableBiPredicate<T, U, E extends Throwable> {
boolean test(T var1, U var2) throws E;
}
@FunctionalInterface
public interface FailableConsumer<T, E extends Throwable> {
@ -76,30 +68,6 @@ public class BeaconClient implements AutoCloseable {
}
}
public <REQ extends RequestMessage, RES extends ResponseMessage> void exchange(
REQ req,
FailableConsumer<OutputStream, IOException> reqWriter,
FailableBiConsumer<RES, InputStream, IOException> resReader)
throws ConnectorException, ClientException, ServerException {
try {
sendRequest(req);
if (reqWriter != null) {
out.write(BODY_SEPARATOR);
reqWriter.accept(out);
}
var res = this.<RES>receiveResponse();
var sep = in.readNBytes(BODY_SEPARATOR.length);
if (sep.length != 0 && !Arrays.equals(BODY_SEPARATOR, sep)) {
throw new ConnectorException("Invalid body separator");
}
resReader.accept(res, in);
} catch (IOException ex) {
throw new ConnectorException("Couldn't communicate with socket", ex);
}
}
public InputStream receiveBody() throws ConnectorException {
try {
var sep = in.readNBytes(BODY_SEPARATOR.length);
@ -238,16 +206,4 @@ public class BeaconClient implements AutoCloseable {
throw new ConnectorException("Couldn't parse response", ex);
}
}
public InputStream getInputStream() {
return in;
}
public OutputStream getOutputStream() {
return out;
}
public Socket getSocket() {
return socket;
}
}

View file

@ -1,8 +1,5 @@
package io.xpipe.beacon;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

View file

@ -13,7 +13,7 @@ import java.nio.file.Path;
import java.util.Optional;
/**
* Contains basic functionality to start, communicate, and stop a beacon server.
* Contains basic functionality to start, communicate, and stop a remote beacon server.
*/
@UtilityClass
public class BeaconServer {

View file

@ -1,4 +1,4 @@
package io.xpipe.beacon.message;
package io.xpipe.beacon;
public interface RequestMessage {

View file

@ -0,0 +1,5 @@
package io.xpipe.beacon;
public interface ResponseMessage {
}

View file

@ -1,11 +1,8 @@
package io.xpipe.beacon;
import lombok.experimental.StandardException;
/**
* Indicates that an internal server error occurred.
*/
@StandardException
public class ServerException extends Exception {
public ServerException() {

View file

@ -1,7 +1,7 @@
package io.xpipe.beacon.exchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.beacon.RequestMessage;
import io.xpipe.beacon.ResponseMessage;
import io.xpipe.core.dialog.DialogReference;
import io.xpipe.core.source.DataSourceId;
import io.xpipe.core.source.DataSourceReference;

View file

@ -1,7 +1,7 @@
package io.xpipe.beacon.exchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.beacon.RequestMessage;
import io.xpipe.beacon.ResponseMessage;
import java.util.Optional;
import java.util.ServiceLoader;
@ -15,26 +15,23 @@ public class MessageExchanges {
private static void loadAll() {
if (ALL == null) {
ALL = ServiceLoader.load(MessageExchange.class).stream()
.map(s -> (MessageExchange) s.get()).collect(Collectors.toSet());
.map(ServiceLoader.Provider::get).collect(Collectors.toSet());
}
}
public static <RQ extends RequestMessage, RP extends ResponseMessage> Optional<MessageExchange> byId(String name) {
public static Optional<MessageExchange> byId(String name) {
loadAll();
var r = ALL.stream().filter(d -> d.getId().equals(name)).findAny();
return Optional.ofNullable((MessageExchange) r.orElse(null));
return ALL.stream().filter(d -> d.getId().equals(name)).findAny();
}
public static <RQ extends RequestMessage, RP extends ResponseMessage> Optional<MessageExchange> byRequest(RQ req) {
public static <RQ extends RequestMessage> Optional<MessageExchange> byRequest(RQ req) {
loadAll();
var r = ALL.stream().filter(d -> d.getRequestClass().equals(req.getClass())).findAny();
return r;
return ALL.stream().filter(d -> d.getRequestClass().equals(req.getClass())).findAny();
}
public static <RQ extends RequestMessage, RP extends ResponseMessage> Optional<MessageExchange> byResponse(RP rep) {
public static <RP extends ResponseMessage> Optional<MessageExchange> byResponse(RP rep) {
loadAll();
var r = ALL.stream().filter(d -> d.getResponseClass().equals(rep.getClass())).findAny();
return r;
return ALL.stream().filter(d -> d.getResponseClass().equals(rep.getClass())).findAny();
}
public static Set<MessageExchange> getAll() {

View file

@ -1,7 +1,7 @@
package io.xpipe.beacon.exchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.beacon.RequestMessage;
import io.xpipe.beacon.ResponseMessage;
import io.xpipe.core.source.DataSourceId;
import io.xpipe.core.source.DataSourceInfo;
import io.xpipe.core.source.DataSourceReference;

View file

@ -1,8 +1,7 @@
package io.xpipe.beacon.exchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.core.source.DataSourceConfigInstance;
import io.xpipe.beacon.RequestMessage;
import io.xpipe.beacon.ResponseMessage;
import io.xpipe.core.source.DataSourceId;
import io.xpipe.core.store.DataStore;
import lombok.Builder;
@ -26,8 +25,6 @@ public class ReadExecuteExchange implements MessageExchange {
public static class Request implements RequestMessage {
@NonNull
DataStore dataStore;
@NonNull
DataSourceConfigInstance config;
DataSourceId target;
}

View file

@ -1,7 +1,7 @@
package io.xpipe.beacon.exchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.beacon.RequestMessage;
import io.xpipe.beacon.ResponseMessage;
import io.xpipe.core.dialog.DialogReference;
import io.xpipe.core.store.DataStore;
import lombok.Builder;

View file

@ -1,7 +1,7 @@
package io.xpipe.beacon.exchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.beacon.RequestMessage;
import io.xpipe.beacon.ResponseMessage;
import lombok.Builder;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;

View file

@ -1,7 +1,7 @@
package io.xpipe.beacon.exchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.beacon.RequestMessage;
import io.xpipe.beacon.ResponseMessage;
import io.xpipe.core.store.FileStore;
import lombok.Builder;
import lombok.Value;

View file

@ -1,8 +1,8 @@
package io.xpipe.beacon.exchange.api;
import io.xpipe.beacon.exchange.MessageExchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.beacon.RequestMessage;
import io.xpipe.beacon.ResponseMessage;
import io.xpipe.core.source.DataSourceReference;
import lombok.Builder;
import lombok.NonNull;

View file

@ -1,8 +1,8 @@
package io.xpipe.beacon.exchange.api;
import io.xpipe.beacon.exchange.MessageExchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.beacon.RequestMessage;
import io.xpipe.beacon.ResponseMessage;
import io.xpipe.core.source.DataSourceReference;
import lombok.Builder;
import lombok.NonNull;

View file

@ -1,8 +1,8 @@
package io.xpipe.beacon.exchange.api;
import io.xpipe.beacon.exchange.MessageExchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.beacon.RequestMessage;
import io.xpipe.beacon.ResponseMessage;
import io.xpipe.core.source.DataSourceReference;
import lombok.Builder;
import lombok.NonNull;

View file

@ -1,8 +1,8 @@
package io.xpipe.beacon.exchange.cli;
import io.xpipe.beacon.exchange.MessageExchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.beacon.RequestMessage;
import io.xpipe.beacon.ResponseMessage;
import io.xpipe.core.dialog.DialogReference;
import io.xpipe.core.source.DataSourceId;
import io.xpipe.core.source.DataSourceReference;

View file

@ -1,8 +1,8 @@
package io.xpipe.beacon.exchange.cli;
import io.xpipe.beacon.exchange.MessageExchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.beacon.RequestMessage;
import io.xpipe.beacon.ResponseMessage;
import io.xpipe.core.dialog.DialogElement;
import lombok.Builder;
import lombok.Value;

View file

@ -2,8 +2,8 @@ package io.xpipe.beacon.exchange.cli;
import io.xpipe.beacon.exchange.MessageExchange;
import io.xpipe.beacon.exchange.data.CollectionListEntry;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.beacon.RequestMessage;
import io.xpipe.beacon.ResponseMessage;
import lombok.Builder;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;

View file

@ -2,8 +2,8 @@ package io.xpipe.beacon.exchange.cli;
import io.xpipe.beacon.exchange.MessageExchange;
import io.xpipe.beacon.exchange.data.EntryListEntry;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.beacon.RequestMessage;
import io.xpipe.beacon.ResponseMessage;
import lombok.Builder;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;

View file

@ -2,8 +2,8 @@ package io.xpipe.beacon.exchange.cli;
import io.xpipe.beacon.exchange.MessageExchange;
import io.xpipe.beacon.exchange.data.StoreListEntry;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.beacon.RequestMessage;
import io.xpipe.beacon.ResponseMessage;
import lombok.Builder;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;

View file

@ -1,8 +1,8 @@
package io.xpipe.beacon.exchange.cli;
import io.xpipe.beacon.exchange.MessageExchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.beacon.RequestMessage;
import io.xpipe.beacon.ResponseMessage;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;

View file

@ -2,8 +2,8 @@ package io.xpipe.beacon.exchange.cli;
import io.xpipe.beacon.exchange.MessageExchange;
import io.xpipe.beacon.exchange.data.ProviderEntry;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.beacon.RequestMessage;
import io.xpipe.beacon.ResponseMessage;
import io.xpipe.core.source.DataSourceType;
import lombok.Builder;
import lombok.NonNull;

View file

@ -1,8 +1,8 @@
package io.xpipe.beacon.exchange.cli;
import io.xpipe.beacon.exchange.MessageExchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.beacon.RequestMessage;
import io.xpipe.beacon.ResponseMessage;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;

View file

@ -1,8 +1,8 @@
package io.xpipe.beacon.exchange.cli;
import io.xpipe.beacon.exchange.MessageExchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.beacon.RequestMessage;
import io.xpipe.beacon.ResponseMessage;
import io.xpipe.core.source.DataSourceId;
import io.xpipe.core.source.DataSourceReference;
import lombok.Builder;

View file

@ -1,8 +1,8 @@
package io.xpipe.beacon.exchange.cli;
import io.xpipe.beacon.exchange.MessageExchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.beacon.RequestMessage;
import io.xpipe.beacon.ResponseMessage;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;

View file

@ -1,8 +1,8 @@
package io.xpipe.beacon.exchange.cli;
import io.xpipe.beacon.exchange.MessageExchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.beacon.RequestMessage;
import io.xpipe.beacon.ResponseMessage;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;

View file

@ -1,8 +1,8 @@
package io.xpipe.beacon.exchange.cli;
import io.xpipe.beacon.exchange.MessageExchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.beacon.RequestMessage;
import io.xpipe.beacon.ResponseMessage;
import io.xpipe.core.source.DataSourceId;
import io.xpipe.core.source.DataSourceReference;
import lombok.Builder;

View file

@ -1,8 +1,8 @@
package io.xpipe.beacon.exchange.cli;
import io.xpipe.beacon.exchange.MessageExchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.beacon.RequestMessage;
import io.xpipe.beacon.ResponseMessage;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;

View file

@ -1,8 +1,8 @@
package io.xpipe.beacon.exchange.cli;
import io.xpipe.beacon.exchange.MessageExchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.beacon.RequestMessage;
import io.xpipe.beacon.ResponseMessage;
import io.xpipe.core.source.DataSourceReference;
import lombok.Builder;
import lombok.NonNull;

View file

@ -1,8 +1,8 @@
package io.xpipe.beacon.exchange.cli;
import io.xpipe.beacon.exchange.MessageExchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.beacon.RequestMessage;
import io.xpipe.beacon.ResponseMessage;
import lombok.Builder;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;

View file

@ -1,8 +1,8 @@
package io.xpipe.beacon.exchange.cli;
import io.xpipe.beacon.exchange.MessageExchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.beacon.RequestMessage;
import io.xpipe.beacon.ResponseMessage;
import io.xpipe.core.dialog.DialogReference;
import io.xpipe.core.store.DataStore;
import lombok.Builder;

View file

@ -1,8 +1,8 @@
package io.xpipe.beacon.exchange.cli;
import io.xpipe.beacon.exchange.MessageExchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.beacon.RequestMessage;
import io.xpipe.beacon.ResponseMessage;
import lombok.Builder;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;

View file

@ -1,9 +1,8 @@
package io.xpipe.beacon.exchange.cli;
import io.xpipe.beacon.exchange.MessageExchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.core.source.DataSourceConfigInstance;
import io.xpipe.beacon.RequestMessage;
import io.xpipe.beacon.ResponseMessage;
import io.xpipe.core.source.DataSourceReference;
import lombok.Builder;
import lombok.NonNull;
@ -26,9 +25,6 @@ public class WriteExecuteExchange implements MessageExchange {
public static class Request implements RequestMessage {
@NonNull
DataSourceReference ref;
@NonNull
DataSourceConfigInstance config;
}
@Jacksonized

View file

@ -1,8 +1,8 @@
package io.xpipe.beacon.exchange.cli;
import io.xpipe.beacon.exchange.MessageExchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.beacon.RequestMessage;
import io.xpipe.beacon.ResponseMessage;
import io.xpipe.core.dialog.DialogReference;
import io.xpipe.core.source.DataSourceReference;
import io.xpipe.core.store.StreamDataStore;

View file

@ -6,6 +6,7 @@ import lombok.Builder;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
@SuppressWarnings("ClassCanBeRecord")
@Value
@Builder
@Jacksonized

View file

@ -8,6 +8,7 @@ import lombok.extern.jackson.Jacksonized;
import java.util.UUID;
@SuppressWarnings("ClassCanBeRecord")
@Value
@Builder
@Jacksonized

View file

@ -1,10 +0,0 @@
package io.xpipe.beacon.message;
import io.xpipe.beacon.BeaconHandler;
public interface ResponseMessage {
default void postSend(BeaconHandler handler) throws Exception {
}
}

View file

@ -5,7 +5,6 @@ import io.xpipe.beacon.exchange.cli.*;
module io.xpipe.beacon {
exports io.xpipe.beacon;
exports io.xpipe.beacon.exchange;
exports io.xpipe.beacon.message;
exports io.xpipe.beacon.exchange.api;
exports io.xpipe.beacon.exchange.data;
exports io.xpipe.beacon.exchange.cli;
@ -13,7 +12,6 @@ module io.xpipe.beacon {
opens io.xpipe.beacon;
opens io.xpipe.beacon.exchange;
opens io.xpipe.beacon.exchange.api;
opens io.xpipe.beacon.message;
opens io.xpipe.beacon.exchange.data;
opens io.xpipe.beacon.exchange.cli;

View file

@ -1,16 +0,0 @@
plugins {
id 'java'
id "org.moditect.gradleplugin" version "1.0.0-rc3"
}
apply from: "$rootDir/deps/java.gradle"
apply from: "$rootDir/deps/commons.gradle"
apply from: "$rootDir/deps/junit.gradle"
apply from: "$rootDir/deps/lombok.gradle"
apply from: "$rootDir/deps/jackson.gradle"
configurations {
compileOnly.extendsFrom(dep)
}

View file

@ -1,8 +0,0 @@
module io.xpipe.charsetter {
exports io.xpipe.charsetter;
requires org.apache.commons.io;
requires org.apache.commons.lang3;
requires static lombok;
requires com.fasterxml.jackson.databind;
}

View file

@ -2,19 +2,20 @@
[![javadoc](https://javadoc.io/badge2/io.xpipe/core/javadoc.svg)](https://javadoc.io/doc/io.xpipe/core)
[![Build Status](https://github.com/xpipe-io/xpipe_java/actions/workflows/core-build.yml/badge.svg)](https://github.com/xpipe-io/xpipe_java/actions/workflows/core-build.yml)
## X-Pipe Core
The X-Pipe core component contains all the shared core classes used by the API, beacon, and daemon.
The X-Pipe core module contains all the shared core classes used by the API, beacon, and daemon implementation.
The main part can be found in the [data package]().
The main component is the [data package](src/main/java/io/xpipe/core/data).
It contains all definitions of the internal X-Pipe data model and all IO functionality for these data structures.
The [source package]() contains the basic data source model classes.
These have to be used by every custom data source implementation.
The [source package](src/main/java/io/xpipe/core/source) contains the basic data source classes,
which are used by every data source implementation.
The [store package]() contains the basic data store model classes.
These have to be used by every custom data store implementation.
The [store package](src/main/java/io/xpipe/core/store) contains the basic data store classes,
which are used by every data store implementation.
Every class is expected to be potentially used in the context of files and message exchanges.
As a result, all data structures exchanged must be serializable/deserializable with jackson.
As a result, all data structures exchanged must be serializable/deserializable with jackson.

View file

@ -14,6 +14,7 @@ apply from: "$rootDir/deps/publish-base.gradle"
configurations {
compileOnly.extendsFrom(dep)
testImplementation.extendsFrom(dep)
}
version = file('../version').text

View file

@ -1,4 +1,4 @@
package io.xpipe.charsetter;
package io.xpipe.core.charsetter;
import java.nio.charset.Charset;

View file

@ -1,10 +1,6 @@
package io.xpipe.charsetter;
package io.xpipe.core.charsetter;
import lombok.Value;
import org.apache.commons.io.ByteOrderMark;
import org.apache.commons.io.input.BOMInputStream;
import org.apache.commons.lang3.function.FailableConsumer;
import org.apache.commons.lang3.function.FailableSupplier;
import java.io.InputStream;
import java.io.InputStreamReader;
@ -15,52 +11,46 @@ import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
public class Charsetter {
public abstract class Charsetter {
private static CharsetterUniverse universe;
private static final int MAX_BYTES = 8192;
public static void init(CharsetterContext ctx) {
universe = CharsetterUniverse.create(ctx);
}
private static void checkInit() {
protected static void checkInit() {
if (universe == null) {
throw new IllegalStateException("Charsetter not initialized");
}
}
public static void init(CharsetterContext ctx) {
universe = CharsetterUniverse.create(ctx);
}
@Value
public static class Result {
Charset charset;
NewLine newLine;
}
public static Result read(FailableSupplier<InputStream, Exception> in, FailableConsumer<InputStreamReader, Exception> con) throws Exception {
checkInit();
public static Charsetter INSTANCE;
try (var is = in.get();
var bin = new BOMInputStream(is)) {
ByteOrderMark bom = bin.getBOM();
String charsetName = bom == null ? null : bom.getCharsetName();
var charset = charsetName != null ? Charset.forName(charsetName) : null;
bin.mark(MAX_BYTES);
var bytes = bin.readNBytes(MAX_BYTES);
bin.reset();
if (charset == null) {
charset = inferCharset(bytes);
}
var nl = inferNewLine(bytes);
if (con != null) {
con.accept(new InputStreamReader(bin, charset));
}
return new Result(charset, nl);
}
public static Charsetter get() {
return INSTANCE;
}
public static NewLine inferNewLine(byte[] content) {
@FunctionalInterface
public interface FailableSupplier<R, E extends Throwable> {
R get() throws E;
}
@FunctionalInterface
public interface FailableConsumer<T, E extends Throwable> {
void accept(T var1) throws E;
}
public abstract Result read(FailableSupplier<InputStream, Exception> in, FailableConsumer<InputStreamReader, Exception> con) throws Exception;
public NewLine inferNewLine(byte[] content) {
Map<NewLine, Integer> count = new HashMap<>();
for (var nl : NewLine.values()) {
var nlBytes = nl.getNewLine().getBytes(StandardCharsets.UTF_8);
@ -75,7 +65,7 @@ public class Charsetter {
.orElseThrow().getKey();
}
public static int count(byte[] outerArray, byte[] smallerArray) {
private static int count(byte[] outerArray, byte[] smallerArray) {
int count = 0;
for(int i = 0; i < outerArray.length - smallerArray.length+1; ++i) {
boolean found = true;
@ -92,7 +82,7 @@ public class Charsetter {
return count;
}
public static Charset inferCharset(byte[] content) {
public Charset inferCharset(byte[] content) {
checkInit();
for (Charset c : universe.getCharsets()) {

View file

@ -1,4 +1,4 @@
package io.xpipe.charsetter;
package io.xpipe.core.charsetter;
import lombok.AllArgsConstructor;
import lombok.Value;

View file

@ -1,4 +1,4 @@
package io.xpipe.charsetter;
package io.xpipe.core.charsetter;
import lombok.AllArgsConstructor;
import lombok.Value;

View file

@ -1,4 +1,4 @@
package io.xpipe.charsetter;
package io.xpipe.core.charsetter;
import com.fasterxml.jackson.annotation.JsonProperty;

View file

@ -79,7 +79,13 @@ public class GenericDataStreamParser {
}
private static void parseValue(InputStream in, GenericDataStreamCallback cb) throws IOException {
var textual = in.read() != 0;
var type = in.read();
if (type == DataStructureNodeIO.VALUE_TYPE_NULL) {
cb.onValue(null, false);
return;
}
var textual = type == DataStructureNodeIO.VALUE_TYPE_TEXT;
var size = in.read();
var data = in.readNBytes(size);
cb.onValue(data, textual);

View file

@ -51,10 +51,15 @@ public class GenericDataStreamWriter {
}
}
private static void writeValue(OutputStream out, ValueNode value) throws IOException {
private static void writeValue(OutputStream out, ValueNode n) throws IOException {
out.write(DataStructureNodeIO.GENERIC_VALUE_ID);
out.write(value.isTextual() ? 1 : 0);
out.write(value.getRawData().length);
out.write(value.getRawData());
if (n.isNull()) {
out.write(DataStructureNodeIO.VALUE_TYPE_NULL);
return;
}
out.write(n.isTextual() ? DataStructureNodeIO.VALUE_TYPE_TEXT : DataStructureNodeIO.VALUE_TYPE_BARE);
out.write(n.getRawData().length);
out.write(n.getRawData());
}
}

View file

@ -12,4 +12,8 @@ public class DataStructureNodeIO {
public static final int TYPED_TUPLE_ID = 6;
public static final int TYPED_ARRAY_ID = 7;
public static final int TYPED_VALUE_ID = 8;
public static final int VALUE_TYPE_BARE = 0;
public static final int VALUE_TYPE_TEXT = 1;
public static final int VALUE_TYPE_NULL = 2;
}

View file

@ -21,9 +21,18 @@ public class MutableValueNode extends ValueNode {
@Override
public String toString(int indent) {
if (isNull()) {
return "null (M)";
}
return (textual ? "\"" : "") + new String(data) + (textual ? "\"" : "") + " (M)";
}
@Override
public boolean isNull() {
return data == null;
}
@Override
public boolean isTextual() {
return textual;

View file

@ -129,7 +129,13 @@ public class TypedDataStreamParser {
}
private void parseValue(InputStream in, TypedDataStreamCallback cb) throws IOException {
var textual = in.read() != 0;
var type = in.read();
if (type == DataStructureNodeIO.VALUE_TYPE_NULL) {
cb.onValue(null, false);
return;
}
var textual = type == DataStructureNodeIO.VALUE_TYPE_TEXT;
var size = in.read();
var data = in.readNBytes(size);
cb.onValue(data, textual);

View file

@ -32,7 +32,12 @@ public class TypedDataStreamWriter {
private static void writeValue(OutputStream out, ValueNode n) throws IOException {
out.write(DataStructureNodeIO.TYPED_VALUE_ID);
out.write(n.isTextual() ? 1 : 0);
if (n.isNull()) {
out.write(DataStructureNodeIO.VALUE_TYPE_NULL);
return;
}
out.write(n.isTextual() ? DataStructureNodeIO.VALUE_TYPE_TEXT : DataStructureNodeIO.VALUE_TYPE_BARE);
out.write(n.getRawData().length);
out.write(n.getRawData());
}

View file

@ -1,26 +0,0 @@
package io.xpipe.core.dialog;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import lombok.Value;
@Value
@AllArgsConstructor
public class ConfigParameter {
String key;
@JsonCreator
public ConfigParameter(String key) {
this.key = key;
this.converter = null;
}
@JsonIgnore
QueryConverter<?> converter;
@SuppressWarnings("unchecked")
public <T> QueryConverter<T> getConverter() {
return (QueryConverter<T>) converter;
}
}

View file

@ -1,54 +0,0 @@
package io.xpipe.core.dialog;
import com.fasterxml.jackson.annotation.JsonCreator;
import lombok.AllArgsConstructor;
import lombok.Value;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
@Value
@AllArgsConstructor(onConstructor_={@JsonCreator})
public class ConfigParameterSetInstance {
/**
* The available configuration parameters.
*/
List<ConfigParameter> configParameters;
/**
* The current configuration options that are set.
*/
Map<String, String> currentValues;
public ConfigParameterSetInstance(Map<ConfigParameter, Object> map) {
configParameters = map.keySet().stream().toList();
currentValues = map.entrySet().stream().collect(Collectors.toMap(
e -> e.getKey().getKey(),
e -> e.getKey().getConverter().convertToString(e.getValue())));
}
public <X, T extends Function<X,?>> ConfigParameterSetInstance(Map<ConfigParameter, T> map, Object v) {
configParameters = map.keySet().stream().toList();
currentValues = map.entrySet().stream().collect(Collectors.toMap(
e -> e.getKey().getKey(),
e -> e.getKey().getConverter().convertToString(apply(e.getValue(), v))));
}
@SuppressWarnings("unchecked")
private static <X, T extends Function<X,?>, V> Object apply(T func, Object v) {
return func.apply((X) v);
}
public void update(ConfigParameter p, String val) {
currentValues.put(p.getKey(), val);
}
public Map<ConfigParameter, Object> evaluate() {
return configParameters.stream().collect(Collectors.toMap(
p -> p,
p -> p.getConverter().convertFromString(currentValues.get(p.getKey()))));
}
}

View file

@ -1,47 +0,0 @@
package io.xpipe.core.source;
import com.fasterxml.jackson.annotation.JsonCreator;
import io.xpipe.core.dialog.ConfigParameter;
import io.xpipe.core.dialog.ConfigParameterSetInstance;
import lombok.AllArgsConstructor;
import lombok.Value;
import java.util.Map;
import java.util.function.Function;
/**
* Represents the current configuration of a data source.
* This configuration can either be in progress or complete.
*/
@Value
@AllArgsConstructor(onConstructor_={@JsonCreator})
public class DataSourceConfigInstance {
public static DataSourceConfigInstance xpbt() {
return new DataSourceConfigInstance("xpbt", new ConfigParameterSetInstance(Map.of()));
}
/**
* The data source provider id.
*/
String provider;
/**
* The available configuration parameters.
*/
ConfigParameterSetInstance configInstance;
public DataSourceConfigInstance(String provider, Map<ConfigParameter, Object> map) {
this.provider = provider;
this.configInstance = new ConfigParameterSetInstance(map);
}
public <X, T extends Function<X,?>> DataSourceConfigInstance(String provider, Map<ConfigParameter, T> map, Object val) {
this.provider = provider;
this.configInstance = new ConfigParameterSetInstance(map, val);
}
public Map<ConfigParameter, Object> evaluate() {
return configInstance.evaluate();
}
}

View file

@ -9,7 +9,6 @@ module io.xpipe.core {
exports io.xpipe.core.data.node;
exports io.xpipe.core.data.typed;
exports io.xpipe.core.dialog;
exports io.xpipe.core.connection;
opens io.xpipe.core.store;
opens io.xpipe.core.source;
@ -19,6 +18,7 @@ module io.xpipe.core {
opens io.xpipe.core.data.node;
opens io.xpipe.core.data.typed;
opens io.xpipe.core.dialog;
exports io.xpipe.core.charsetter;
requires com.fasterxml.jackson.core;
requires com.fasterxml.jackson.databind;

View file

@ -5,16 +5,14 @@
## X-Pipe Extension API
The X-Pipe extension API allows you to create extensions of any kind for X-Pipe.
This includes:
- Custom data stores, including configuration GUI and CLI
- Custom data sources, including configuration GUI and CLI
- Custom preferences entries
### Custom data sources
A custom data source type can be implemented by creating a custom [DataSourceProvider]().
A custom data source type can be implemented by creating a custom
[DataSourceProvider](src/main/java/io/xpipe/extension/DataSourceProvider.java).
This provider contains all the information required for proper handling of your custom data sources,
whether you access it from the CLI, any API, or the X-Pipe commander gui.
### Custom data sinks
A custom data sink type can be implemented by creating a custom [DataSinkProvider]().
As the notion of a sink is abstract, it allows you to basically implement any type of sink you want.
Whether the target is a programming language, another application, a database, or a regular file.

View file

@ -32,7 +32,6 @@ repositories {
dependencies {
compileOnly 'org.junit.jupiter:junit-jupiter-api:5.8.2'
implementation project(':core')
implementation project(':charsetter')
implementation 'io.xpipe:fxcomps:0.1'
implementation 'com.google.code.gson:gson:2.9.0'

View file

@ -1,6 +1,6 @@
package io.xpipe.extension;
import io.xpipe.charsetter.NewLine;
import io.xpipe.core.charsetter.NewLine;
import io.xpipe.core.dialog.Dialog;
import io.xpipe.core.dialog.QueryConverter;
import io.xpipe.core.source.DataSource;

View file

@ -1,7 +1,7 @@
package io.xpipe.extension;
import io.xpipe.charsetter.Charsetter;
import io.xpipe.charsetter.CharsetterContext;
import io.xpipe.core.charsetter.Charsetter;
import io.xpipe.core.charsetter.CharsetterContext;
import io.xpipe.core.util.JacksonHelper;
import io.xpipe.extension.util.ModuleHelper;
import org.junit.jupiter.api.AfterAll;

View file

@ -1,6 +1,6 @@
package io.xpipe.extension.comp;
import io.xpipe.charsetter.NewLine;
import io.xpipe.core.charsetter.NewLine;
import io.xpipe.core.source.DataSource;
import io.xpipe.extension.I18n;
import io.xpipe.fxcomps.Comp;

View file

@ -28,7 +28,6 @@ module io.xpipe.extension {
requires org.kordamp.ikonli.javafx;
requires com.fasterxml.jackson.databind;
requires static org.junit.jupiter.api;
requires io.xpipe.charsetter;
uses DataSourceProvider;
uses SupportedApplicationProvider;

100
jreleaser.gradle Normal file
View file

@ -0,0 +1,100 @@
def isPreRelease = project.version.endsWith('-pre')
def isFullRelease = !isPreRelease && !project.version.endsWith('-SNAPSHOT')
def proj = project
def canonicalVersion = file('canonical_version').text
jreleaser {
environment {
properties.put('rawChangelog', file("changelogs/${canonicalVersion}.txt").exists() ?
file("changelogs/${canonicalVersion}.txt").text.replace('\r\n', '\n') : "")
}
project {
name = 'Pdx-Unlimiter'
description = 'A smart savegame manager, editor, and toolbox for all current major Paradox Grand Strategy games.'
longDescription = 'The Pdx-Unlimiter is a tool for all major Paradox Grand Strategy games that provides a ' +
'powerful and smart savegame manager to quickly organize and play all of your savegames with ease. ' +
'Furthermore, it also comes with an Ironman converter, a powerful savegame editor, some savescumming ' +
'tools, integrations for various other great community-made tools for all major Paradox games.'
website = 'https://github.com/crschnick/pdx_unlimiter'
authors = ['Christopher Schnick']
license = 'GPL3'
copyright = ' '
java {
groupId = 'com.crschnick.pdxu'
version = '17'
multiProject = true
}
snapshot {
enabled = true
pattern = '.*-SNAPSHOT'
label = 'early-access'
}
}
release {
github {
skipRelease = !isFullRelease && !isPreRelease
skipTag = !isFullRelease && !isPreRelease
owner = 'crschnick'
overwrite = false
tagName = '{{projectVersion}}'
releaseName = '{{tagName}}'
token = proj.hasProperty("PDXU_GITHUB_TOKEN") ? proj.property("PDXU_GITHUB_TOKEN") : System.getenv("JRELEASER_GITHUB_TOKEN")
files = true
artifacts = true
checksums = false
signatures = false
// Always set a pre-release, as drafts do not fully work
prerelease {
enabled = true
pattern = '.*'
}
update {
enabled = true
section('ASSETS')
section('TITLE')
section('BODY')
}
changelog {
enabled = true
formatted = 'ALWAYS'
contentTemplate = isFullRelease ? file('misc/github_full.tpl') : file('misc/github_pre.tpl')
}
}
}
distributions {
app {
if (org.gradle.internal.os.OperatingSystem.current().isWindows()) {
artifact {
distributionType = 'BINARY'
path = 'build/pdx_unlimiter-windows.zip'
platform = 'windows'
}
} else {
artifact {
distributionType = 'BINARY'
path = 'build/pdx_unlimiter-linux.zip'
platform = 'linux'
}
}
}
}
announce {
enabled = isFullRelease || isPreRelease
discord {
active = 'ALWAYS'
webhook = proj.hasProperty("PDXU_DISCORD_WEBHOOK") ? proj.property("PDXU_DISCORD_WEBHOOK") : System.getenv("JRELEASER_DISCORD_WEBHOOK")
messageTemplate = isFullRelease ? 'misc/discord_full.tpl' : 'misc/discord_pre.tpl'
}
}
}