mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-25 09:00:26 +00:00
Rework exchanges, rework stores, add dialog API
This commit is contained in:
parent
b8164339d0
commit
9440037f03
46 changed files with 737 additions and 454 deletions
|
@ -2,7 +2,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.core.dialog.DialogReference;
|
||||
import io.xpipe.core.source.DataSourceId;
|
||||
import io.xpipe.core.source.DataSourceReference;
|
||||
import lombok.Builder;
|
||||
|
@ -11,13 +11,13 @@ import lombok.Value;
|
|||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
/**
|
||||
* Performs an edit for a data source.
|
||||
* Requests to edit a data source.
|
||||
*/
|
||||
public class EditExecuteExchange implements MessageExchange {
|
||||
public class EditExchange implements MessageExchange {
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "editExecute";
|
||||
return "edit";
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
|
@ -26,15 +26,15 @@ public class EditExecuteExchange implements MessageExchange {
|
|||
public static class Request implements RequestMessage {
|
||||
@NonNull
|
||||
DataSourceReference ref;
|
||||
|
||||
@NonNull
|
||||
DataSourceConfigInstance config;
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response implements ResponseMessage {
|
||||
@NonNull
|
||||
DialogReference config;
|
||||
|
||||
DataSourceId id;
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
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.core.source.DataSourceReference;
|
||||
import lombok.Builder;
|
||||
import lombok.NonNull;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
/**
|
||||
* Requests to edit a data source.
|
||||
*/
|
||||
public class EditPreparationExchange implements MessageExchange {
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "editPreparation";
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Request implements RequestMessage {
|
||||
@NonNull
|
||||
DataSourceReference ref;
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response implements ResponseMessage {
|
||||
DataSourceConfigInstance config;
|
||||
}
|
||||
}
|
|
@ -2,13 +2,17 @@ package io.xpipe.beacon.exchange;
|
|||
|
||||
import io.xpipe.beacon.message.RequestMessage;
|
||||
import io.xpipe.beacon.message.ResponseMessage;
|
||||
import io.xpipe.core.source.*;
|
||||
import io.xpipe.core.source.DataSourceId;
|
||||
import io.xpipe.core.source.DataSourceInfo;
|
||||
import io.xpipe.core.source.DataSourceReference;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
import lombok.Builder;
|
||||
import lombok.NonNull;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Queries general information about a data source.
|
||||
*/
|
||||
|
@ -38,6 +42,8 @@ public class QueryDataSourceExchange implements MessageExchange {
|
|||
@NonNull
|
||||
DataStore store;
|
||||
@NonNull
|
||||
DataSourceConfigInstance config;
|
||||
String provider;
|
||||
@NonNull
|
||||
Map<String, String> config;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,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.core.dialog.DialogReference;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
import lombok.Builder;
|
||||
import lombok.NonNull;
|
||||
|
@ -38,6 +38,6 @@ public class ReadPreparationExchange implements MessageExchange {
|
|||
String determinedType;
|
||||
|
||||
@NonNull
|
||||
DataSourceConfigInstance config;
|
||||
DialogReference config;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ package io.xpipe.beacon.exchange;
|
|||
|
||||
import io.xpipe.beacon.message.RequestMessage;
|
||||
import io.xpipe.beacon.message.ResponseMessage;
|
||||
import io.xpipe.core.store.StreamDataStore;
|
||||
import io.xpipe.core.store.FileStore;
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
@ -27,6 +27,6 @@ public class StoreStreamExchange implements MessageExchange {
|
|||
@Builder
|
||||
@Value
|
||||
public static class Response implements ResponseMessage {
|
||||
StreamDataStore store;
|
||||
FileStore store;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ 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.core.dialog.DialogReference;
|
||||
import io.xpipe.core.source.DataSourceId;
|
||||
import io.xpipe.core.source.DataSourceReference;
|
||||
import io.xpipe.core.source.DataSourceType;
|
||||
|
@ -38,6 +38,6 @@ public class ConvertExchange implements MessageExchange {
|
|||
@Value
|
||||
public static class Response implements ResponseMessage {
|
||||
@NonNull
|
||||
DataSourceConfigInstance config;
|
||||
DialogReference config;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
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 lombok.Builder;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ListStoresExchange implements MessageExchange {
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "listStores";
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Request implements RequestMessage {
|
||||
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response implements ResponseMessage {
|
||||
List<StoreListEntry> entries;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package io.xpipe.beacon.exchange;
|
||||
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 lombok.Builder;
|
|
@ -1,5 +1,6 @@
|
|||
package io.xpipe.beacon.exchange;
|
||||
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.DataSourceId;
|
|
@ -0,0 +1,33 @@
|
|||
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 lombok.Builder;
|
||||
import lombok.NonNull;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
public class RemoveStoreExchange implements MessageExchange {
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "removeStore";
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Request implements RequestMessage {
|
||||
@NonNull
|
||||
String storeName;
|
||||
|
||||
boolean removeUnderlying;
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response implements ResponseMessage {
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package io.xpipe.beacon.exchange;
|
||||
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 lombok.Builder;
|
|
@ -1,5 +1,6 @@
|
|||
package io.xpipe.beacon.exchange;
|
||||
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.DataSourceId;
|
|
@ -0,0 +1,33 @@
|
|||
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 lombok.Builder;
|
||||
import lombok.NonNull;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
public class RenameStoreExchange implements MessageExchange {
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "renameStore";
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Request implements RequestMessage {
|
||||
@NonNull
|
||||
String storeName;
|
||||
@NonNull
|
||||
String newName;
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response implements ResponseMessage {
|
||||
}
|
||||
}
|
|
@ -3,14 +3,12 @@ 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.dialog.DialogElement;
|
||||
import io.xpipe.core.dialog.DialogReference;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
import lombok.Builder;
|
||||
import lombok.NonNull;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class StoreAddExchange implements MessageExchange {
|
||||
|
||||
@Override
|
||||
|
@ -22,15 +20,17 @@ public class StoreAddExchange implements MessageExchange {
|
|||
@Builder
|
||||
@Value
|
||||
public static class Request implements RequestMessage {
|
||||
@NonNull
|
||||
String input;
|
||||
DataStore storeInput;
|
||||
|
||||
String type;
|
||||
String name;
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response implements ResponseMessage {
|
||||
UUID dialogKey;
|
||||
DialogElement dialogElement;
|
||||
DialogReference config;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ 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.core.dialog.DialogReference;
|
||||
import io.xpipe.core.source.DataSourceReference;
|
||||
import io.xpipe.core.store.StreamDataStore;
|
||||
import lombok.Builder;
|
||||
|
@ -37,6 +37,6 @@ public class WritePreparationExchange implements MessageExchange {
|
|||
@Value
|
||||
public static class Response implements ResponseMessage {
|
||||
@NonNull
|
||||
DataSourceConfigInstance config;
|
||||
DialogReference config;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
package io.xpipe.beacon.exchange.data;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
@Value
|
||||
@Jacksonized
|
||||
@Builder
|
||||
public class StoreListEntry {
|
||||
|
||||
String name;
|
||||
String type;
|
||||
}
|
|
@ -29,6 +29,8 @@ module io.xpipe.beacon {
|
|||
ModeExchange,
|
||||
StatusExchange,
|
||||
StopExchange,
|
||||
RenameStoreExchange,
|
||||
RemoveStoreExchange,
|
||||
StoreAddExchange,
|
||||
WritePreparationExchange,
|
||||
WriteExecuteExchange,
|
||||
|
@ -36,11 +38,11 @@ module io.xpipe.beacon {
|
|||
ReadPreparationExchange,
|
||||
QueryTextDataExchange,
|
||||
ReadExecuteExchange,
|
||||
ListStoresExchange,
|
||||
DialogExchange,
|
||||
QueryDataSourceExchange,
|
||||
StoreStreamExchange,
|
||||
EditPreparationExchange,
|
||||
EditExecuteExchange,
|
||||
EditExchange,
|
||||
RemoveEntryExchange,
|
||||
RemoveCollectionExchange,
|
||||
RenameCollectionExchange,
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
package io.xpipe.core.connection;
|
||||
|
||||
public class Connection {
|
||||
}
|
|
@ -9,13 +9,21 @@ import lombok.Getter;
|
|||
public class BaseQueryElement extends DialogElement {
|
||||
|
||||
private final String description;
|
||||
private final boolean newLine;
|
||||
private final boolean required;
|
||||
private final boolean hidden;
|
||||
protected String value;
|
||||
|
||||
@JsonCreator
|
||||
public BaseQueryElement(String description, boolean required, String value) {
|
||||
public BaseQueryElement(String description, boolean newLine, boolean required, boolean hidden, String value) {
|
||||
this.description = description;
|
||||
this.newLine = newLine;
|
||||
this.required = required;
|
||||
this.hidden = hidden;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public boolean isNewLine() {
|
||||
return newLine;
|
||||
}
|
||||
}
|
||||
|
|
12
core/src/main/java/io/xpipe/core/dialog/BusyElement.java
Normal file
12
core/src/main/java/io/xpipe/core/dialog/BusyElement.java
Normal file
|
@ -0,0 +1,12 @@
|
|||
package io.xpipe.core.dialog;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
|
||||
@JsonTypeName("busy")
|
||||
public class BusyElement extends DialogElement {
|
||||
|
||||
@Override
|
||||
public boolean apply(String value) {
|
||||
return true;
|
||||
}
|
||||
}
|
15
core/src/main/java/io/xpipe/core/dialog/Choice.java
Normal file
15
core/src/main/java/io/xpipe/core/dialog/Choice.java
Normal file
|
@ -0,0 +1,15 @@
|
|||
package io.xpipe.core.dialog;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
@Value
|
||||
@Builder
|
||||
@Jacksonized
|
||||
@AllArgsConstructor
|
||||
public class Choice {
|
||||
Character character;
|
||||
String description;
|
||||
}
|
|
@ -2,17 +2,14 @@ package io.xpipe.core.dialog;
|
|||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@JsonTypeName("choice")
|
||||
public class ChoiceElement extends DialogElement {
|
||||
|
||||
private final List<Element> elements;
|
||||
private final String description;
|
||||
private final List<Choice> elements;
|
||||
|
||||
private int selected;
|
||||
|
||||
|
@ -42,26 +39,22 @@ public class ChoiceElement extends DialogElement {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Value
|
||||
@Builder
|
||||
@Jacksonized
|
||||
@AllArgsConstructor
|
||||
public static class Element {
|
||||
Character character;
|
||||
String description;
|
||||
}
|
||||
|
||||
@JsonCreator
|
||||
public ChoiceElement(List<Element> elements, int selected) {
|
||||
public ChoiceElement(String description, List<Choice> elements, int selected) {
|
||||
this.description = description;
|
||||
this.elements = elements;
|
||||
this.selected = selected;
|
||||
}
|
||||
|
||||
public List<Element> getElements() {
|
||||
public List<Choice> getElements() {
|
||||
return elements;
|
||||
}
|
||||
|
||||
public int getSelected() {
|
||||
return selected;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,42 +1,112 @@
|
|||
package io.xpipe.core.dialog;
|
||||
|
||||
import io.xpipe.core.util.Secret;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public abstract class Dialog {
|
||||
|
||||
private static class Sequence extends Dialog {
|
||||
|
||||
private int index = 0;
|
||||
private final DialogElement[] es;
|
||||
|
||||
public Sequence(DialogElement... es) {
|
||||
this.es = es;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DialogElement start() {
|
||||
index = 0;
|
||||
return es[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public DialogElement receive(String answer) {
|
||||
if (es[index].apply(answer)) {
|
||||
if (index == es.length - 1) {
|
||||
complete();
|
||||
return null;
|
||||
} else {
|
||||
return es[++index];
|
||||
}
|
||||
public static Dialog empty() {
|
||||
return new Dialog() {
|
||||
@Override
|
||||
public DialogElement start() throws Exception {
|
||||
return null;
|
||||
}
|
||||
|
||||
return es[index];
|
||||
@Override
|
||||
protected DialogElement next(String answer) throws Exception {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static class Choice extends Dialog {
|
||||
|
||||
private final ChoiceElement element;
|
||||
|
||||
private Choice(String description, List<io.xpipe.core.dialog.Choice> elements, int selected) {
|
||||
this.element = new ChoiceElement(description, elements, selected);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DialogElement start() throws Exception {
|
||||
return element;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DialogElement next(String answer) throws Exception {
|
||||
if (element.apply(answer)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
private int getSelected() {
|
||||
return element.getSelected();
|
||||
}
|
||||
}
|
||||
|
||||
public static Dialog chain(DialogElement... es) {
|
||||
return new Dialog.Sequence(es);
|
||||
public static Dialog.Choice choice(String description, List<io.xpipe.core.dialog.Choice> elements, int selected) {
|
||||
Dialog.Choice c = new Dialog.Choice(description, elements, selected);
|
||||
c.evaluateTo(c::getSelected);
|
||||
return c;
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public static <T> Dialog.Choice choice(String description, Function<T, String> toString, T def, T... vals) {
|
||||
var elements = Arrays.stream(vals).map(v -> new io.xpipe.core.dialog.Choice(null, toString.apply(v))).toList();
|
||||
var index = Arrays.asList(vals).indexOf(def);
|
||||
var c = choice(description, elements, index);
|
||||
c.evaluateTo(() -> vals[c.getSelected()]);
|
||||
return c;
|
||||
}
|
||||
|
||||
public static class Query extends Dialog {
|
||||
|
||||
private final QueryElement element;
|
||||
|
||||
private Query(String description, boolean newLine, boolean required, Object value, QueryConverter<?> converter, boolean hidden) {
|
||||
this.element = new QueryElement(description, newLine, required,value, converter, hidden);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Map.Entry<String, String>> toValue() {
|
||||
return Optional.of(new AbstractMap.SimpleEntry<>(element.getDescription(), element.getValue()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DialogElement start() throws Exception {
|
||||
return element;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DialogElement next(String answer) throws Exception {
|
||||
if (element.apply(answer)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
private <T> T getConvertedValue() {
|
||||
return element.getConvertedValue();
|
||||
}
|
||||
}
|
||||
|
||||
public static Dialog.Query query(String description, boolean newLine, boolean required, Object value, QueryConverter<?> converter) {
|
||||
var q = new Dialog.Query(description, newLine, required, value, converter, false);
|
||||
q.evaluateTo(q::getConvertedValue);
|
||||
return q;
|
||||
}
|
||||
public static Dialog.Query querySecret(String description, boolean newLine, boolean required, Secret value) {
|
||||
var q = new Dialog.Query(description, newLine, required, value, QueryConverter.SECRET, true);
|
||||
q.evaluateTo(q::getConvertedValue);
|
||||
return q;
|
||||
}
|
||||
|
||||
public static Dialog chain(Dialog... ds) {
|
||||
|
@ -45,50 +115,100 @@ public abstract class Dialog {
|
|||
private int current = 0;
|
||||
|
||||
@Override
|
||||
public DialogElement start() {
|
||||
public DialogElement start() throws Exception {
|
||||
current = 0;
|
||||
eval = null;
|
||||
return ds[0].start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DialogElement receive(String answer) {
|
||||
protected DialogElement next(String answer) throws Exception {
|
||||
DialogElement currentElement = ds[current].receive(answer);
|
||||
if (currentElement == null) {
|
||||
ds[current].complete();
|
||||
if (current == ds.length - 1) {
|
||||
complete();
|
||||
return null;
|
||||
} else {
|
||||
return ds[++current].start();
|
||||
}
|
||||
DialogElement next = null;
|
||||
while (current < ds.length - 1 && (next = ds[++current].start()) == null) {
|
||||
|
||||
};
|
||||
return next;
|
||||
}
|
||||
|
||||
return currentElement;
|
||||
}
|
||||
};
|
||||
}.evaluateTo(ds[ds.length - 1]);
|
||||
}
|
||||
|
||||
public static Dialog repeatIf(Dialog d, Supplier<Boolean> shouldRepeat) {
|
||||
public static <T> Dialog repeatIf(Dialog d, Predicate<T> shouldRepeat) {
|
||||
return new Dialog() {
|
||||
|
||||
|
||||
@Override
|
||||
public DialogElement start() {
|
||||
public DialogElement start() throws Exception {
|
||||
eval = null;
|
||||
return d.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DialogElement receive(String answer) {
|
||||
protected DialogElement next(String answer) throws Exception {
|
||||
var next = d.receive(answer);
|
||||
if (next == null) {
|
||||
if (shouldRepeat.get()) {
|
||||
if (shouldRepeat.test(d.getResult())) {
|
||||
return d.start();
|
||||
}
|
||||
}
|
||||
|
||||
return next;
|
||||
}
|
||||
}.evaluateTo(d.onCompletion);
|
||||
}.evaluateTo(d).onCompletion(d.completion);
|
||||
}
|
||||
|
||||
public static Dialog header(String msg) {
|
||||
return of(new HeaderElement(msg)).evaluateTo(() -> msg);
|
||||
}
|
||||
|
||||
public static Dialog header(Supplier<String> msg) {
|
||||
final String[] msgEval = {null};
|
||||
return new Dialog() {
|
||||
@Override
|
||||
public DialogElement start() throws Exception {
|
||||
msgEval[0] = msg.get();
|
||||
return new HeaderElement(msgEval[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DialogElement next(String answer) throws Exception {
|
||||
return null;
|
||||
}
|
||||
}.evaluateTo(() -> msgEval[0]);
|
||||
}
|
||||
|
||||
|
||||
public static Dialog busy() {
|
||||
return of(new BusyElement());
|
||||
}
|
||||
|
||||
public static interface FailableSupplier<T> {
|
||||
|
||||
T get() throws Exception;
|
||||
}
|
||||
|
||||
public static Dialog lazy(FailableSupplier<Dialog> d) {
|
||||
return new Dialog() {
|
||||
|
||||
Dialog dialog;
|
||||
|
||||
@Override
|
||||
public DialogElement start() throws Exception {
|
||||
eval = null;
|
||||
dialog = d.get();
|
||||
evaluateTo(dialog);
|
||||
return dialog.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DialogElement next(String answer) throws Exception {
|
||||
return dialog.receive(answer);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static Dialog of(DialogElement e) {
|
||||
|
@ -96,14 +216,14 @@ public abstract class Dialog {
|
|||
|
||||
|
||||
@Override
|
||||
public DialogElement start() {
|
||||
public DialogElement start() throws Exception {
|
||||
eval = null;
|
||||
return e;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DialogElement receive(String answer) {
|
||||
protected DialogElement next(String answer) throws Exception {
|
||||
if (e.apply(answer)) {
|
||||
complete();
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -112,18 +232,38 @@ public abstract class Dialog {
|
|||
};
|
||||
}
|
||||
|
||||
public static Dialog retryIf(Dialog d, Supplier<String> msg) {
|
||||
|
||||
public static Dialog skipIf(Dialog d, Supplier<Boolean> check) {
|
||||
return new Dialog() {
|
||||
|
||||
private Dialog active;
|
||||
|
||||
@Override
|
||||
public DialogElement start() throws Exception {
|
||||
active = check.get() ? null : d;
|
||||
return active != null ? active.start() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DialogElement next(String answer) throws Exception {
|
||||
return active != null ? active.receive(answer) : null;
|
||||
}
|
||||
}.evaluateTo(d).onCompletion(d.completion);
|
||||
}
|
||||
|
||||
public static <T> Dialog retryIf(Dialog d, Function<T, String> msg) {
|
||||
return new Dialog() {
|
||||
|
||||
private boolean retry;
|
||||
|
||||
@Override
|
||||
public DialogElement start() {
|
||||
public DialogElement start() throws Exception {
|
||||
eval = null;
|
||||
return d.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DialogElement receive(String answer) {
|
||||
protected DialogElement next(String answer) throws Exception {
|
||||
if (retry) {
|
||||
retry = false;
|
||||
return d.start();
|
||||
|
@ -131,7 +271,7 @@ public abstract class Dialog {
|
|||
|
||||
var next = d.receive(answer);
|
||||
if (next == null) {
|
||||
var s = msg.get();
|
||||
var s = msg.apply(d.getResult());
|
||||
if (s != null) {
|
||||
retry = true;
|
||||
return new HeaderElement(s);
|
||||
|
@ -140,47 +280,78 @@ public abstract class Dialog {
|
|||
|
||||
return next;
|
||||
}
|
||||
}.evaluateTo(d.onCompletion);
|
||||
}.evaluateTo(d.evaluation).onCompletion(d.completion);
|
||||
}
|
||||
|
||||
public static Dialog choice(ChoiceElement choice, Function<Integer, Dialog> c) {
|
||||
public static Dialog fork(String description, List<io.xpipe.core.dialog.Choice> elements, int selected, Function<Integer, Dialog> c) {
|
||||
var choice = new ChoiceElement(description, elements, selected);
|
||||
return new Dialog() {
|
||||
|
||||
private Dialog choiceMade;
|
||||
|
||||
@Override
|
||||
public DialogElement start() {
|
||||
public DialogElement start() throws Exception {
|
||||
choiceMade = null;
|
||||
eval = null;
|
||||
return choice;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DialogElement receive(String answer) {
|
||||
protected DialogElement next(String answer) throws Exception {
|
||||
if (choiceMade != null) {
|
||||
var r = choiceMade.receive(answer);
|
||||
if (r == null) {
|
||||
complete();
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
if (choice.apply(answer)) {
|
||||
choiceMade = c.apply(choice.getSelected());
|
||||
return choiceMade.start();
|
||||
return choiceMade != null ? choiceMade.start() : null;
|
||||
}
|
||||
|
||||
return choice;
|
||||
}
|
||||
};
|
||||
}.evaluateTo(() -> choice.getSelected());
|
||||
}
|
||||
|
||||
private Object eval;
|
||||
private Supplier<?> onCompletion;
|
||||
protected Object eval;
|
||||
private Supplier<?> evaluation;
|
||||
private final List<Consumer<?>> completion = new ArrayList<>();
|
||||
|
||||
public abstract DialogElement start();
|
||||
public abstract DialogElement start() throws Exception;
|
||||
|
||||
public Optional<Map.Entry<String, String>> toValue() {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public Dialog evaluateTo(Dialog d) {
|
||||
evaluation = d.evaluation;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Dialog evaluateTo(Supplier<?> s) {
|
||||
onCompletion = s;
|
||||
evaluation = s;
|
||||
return this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> Dialog map(Function<T, ?> s) {
|
||||
var oldEval = evaluation;
|
||||
evaluation = () -> s.apply((T) oldEval.get());
|
||||
return this;
|
||||
}
|
||||
|
||||
public Dialog onCompletion(Consumer<?> s) {
|
||||
completion.add(s);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Dialog onCompletion(Runnable r) {
|
||||
completion.add(v -> r.run());
|
||||
return this;
|
||||
}
|
||||
|
||||
public Dialog onCompletion(List<Consumer<?>> s) {
|
||||
completion.addAll(s);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -189,11 +360,24 @@ public abstract class Dialog {
|
|||
return (T) eval;
|
||||
}
|
||||
|
||||
public void complete() {
|
||||
if (onCompletion != null) {
|
||||
eval = onCompletion.get();
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> void complete() {
|
||||
if (evaluation != null) {
|
||||
eval = evaluation.get();
|
||||
completion.forEach(c -> {
|
||||
Consumer<T> ct = (Consumer<T>) c;
|
||||
ct.accept((T) eval);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public abstract DialogElement receive(String answer);
|
||||
public final DialogElement receive(String answer) throws Exception {
|
||||
var next = next(answer);
|
||||
if (next == null) {
|
||||
complete();
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
protected abstract DialogElement next(String answer) throws Exception;
|
||||
}
|
||||
|
|
18
core/src/main/java/io/xpipe/core/dialog/DialogReference.java
Normal file
18
core/src/main/java/io/xpipe/core/dialog/DialogReference.java
Normal file
|
@ -0,0 +1,18 @@
|
|||
package io.xpipe.core.dialog;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Value
|
||||
@Builder
|
||||
@Jacksonized
|
||||
@AllArgsConstructor
|
||||
public class DialogReference {
|
||||
|
||||
UUID dialogId;
|
||||
DialogElement start;
|
||||
}
|
|
@ -1,8 +1,12 @@
|
|||
package io.xpipe.core.dialog;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import io.xpipe.core.util.Secret;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class QueryConverter<T> {
|
||||
|
||||
|
@ -30,18 +34,51 @@ public abstract class QueryConverter<T> {
|
|||
}
|
||||
};
|
||||
|
||||
public static final QueryConverter<URL> URL = new QueryConverter<URL>() {
|
||||
public static final QueryConverter<Secret> SECRET = new QueryConverter<Secret>() {
|
||||
@Override
|
||||
protected URL fromString(String s) {
|
||||
protected Secret fromString(String s) {
|
||||
return Secret.parse(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String toString(Secret value) {
|
||||
return value.getValue();
|
||||
}
|
||||
};
|
||||
|
||||
public static final QueryConverter<Map.Entry<String, String>> HTTP_HEADER = new QueryConverter<Map.Entry<String, String>>() {
|
||||
@Override
|
||||
protected Map.Entry<String, String> fromString(String s) {
|
||||
if (!s.contains(":")) {
|
||||
throw new IllegalArgumentException("Missing colon");
|
||||
}
|
||||
|
||||
var split = s.split(":");
|
||||
if (split.length != 2) {
|
||||
throw new IllegalArgumentException("Too many colons");
|
||||
}
|
||||
|
||||
return new AbstractMap.SimpleEntry<>(split[0].trim(), split[1].trim());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String toString(Map.Entry<String, String> value) {
|
||||
return value.getKey() + ": " + value.getValue();
|
||||
}
|
||||
};
|
||||
|
||||
public static final QueryConverter<URI> URI = new QueryConverter<URI>() {
|
||||
@Override
|
||||
protected URI fromString(String s) {
|
||||
try {
|
||||
return new URL(s);
|
||||
} catch (MalformedURLException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
return new URI(s);
|
||||
} catch (URISyntaxException e) {
|
||||
throw new IllegalArgumentException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String toString(URL value) {
|
||||
protected String toString(URI value) {
|
||||
return value.toString();
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,21 +1,14 @@
|
|||
package io.xpipe.core.dialog;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
|
||||
@JsonSerialize(as = BaseQueryElement.class)
|
||||
public class QueryElement extends BaseQueryElement {
|
||||
|
||||
@JsonIgnore
|
||||
private final QueryConverter<?> converter;
|
||||
|
||||
public QueryElement(String description, boolean required, String value, QueryConverter<?> converter) {
|
||||
super(description, required, value);
|
||||
this.converter = converter;
|
||||
}
|
||||
|
||||
public QueryElement(String description, boolean required, Object value, QueryConverter<?> converter) {
|
||||
super(description, required, value != null ? value.toString() : null);
|
||||
public QueryElement(String description, boolean newLine, boolean required, Object value, QueryConverter<?> converter, boolean hidden) {
|
||||
super(description, newLine, required, hidden, value != null ? value.toString() : null);
|
||||
this.converter = converter;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package io.xpipe.core.store;
|
||||
|
||||
public abstract class CollectionEntryDataStore implements FileDataStore {
|
||||
public abstract class CollectionEntryDataStore implements StreamDataStore, FilenameStore {
|
||||
|
||||
private final boolean directory;
|
||||
private final String name;
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
package io.xpipe.core.store;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import lombok.Value;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
@Value
|
||||
@JsonTypeName("commandInput")
|
||||
public class CommandInputStore implements StreamDataStore {
|
||||
|
||||
String cmd;
|
||||
|
||||
@Override
|
||||
public InputStream openInput() throws Exception {
|
||||
var proc = Runtime.getRuntime().exec(cmd);
|
||||
return proc.getInputStream();
|
||||
}
|
||||
}
|
|
@ -16,6 +16,17 @@ import java.util.Optional;
|
|||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
|
||||
public interface DataStore {
|
||||
|
||||
default void validate() throws Exception {
|
||||
}
|
||||
|
||||
default boolean delete() throws Exception {
|
||||
return false;
|
||||
}
|
||||
|
||||
default String toDisplay() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Casts this instance to the required type without checking whether a cast is possible.
|
||||
*/
|
||||
|
|
44
core/src/main/java/io/xpipe/core/store/FileStore.java
Normal file
44
core/src/main/java/io/xpipe/core/store/FileStore.java
Normal file
|
@ -0,0 +1,44 @@
|
|||
package io.xpipe.core.store;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import lombok.Value;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
@Value
|
||||
@JsonTypeName("file")
|
||||
public class FileStore implements StreamDataStore, FilenameStore {
|
||||
|
||||
public static FileStore local(Path p) {
|
||||
return new FileStore(MachineStore.local(), p.toString());
|
||||
}
|
||||
|
||||
public static FileStore local(String p) {
|
||||
return new FileStore(MachineStore.local(), p);
|
||||
}
|
||||
|
||||
MachineStore machine;
|
||||
String file;
|
||||
|
||||
@JsonCreator
|
||||
public FileStore(MachineStore machine, String file) {
|
||||
this.machine = machine;
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toDisplay() {
|
||||
return file + "@" + machine.toDisplay();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean persistent() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFileName() {
|
||||
return file;
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@ package io.xpipe.core.store;
|
|||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface FileDataStore extends StreamDataStore {
|
||||
public interface FilenameStore extends DataStore {
|
||||
|
||||
@Override
|
||||
default Optional<String> determineDefaultName() {
|
||||
|
@ -11,10 +11,5 @@ public interface FileDataStore extends StreamDataStore {
|
|||
return Optional.of(i != -1 ? n.substring(0, i) : n);
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean persistent() {
|
||||
return true;
|
||||
}
|
||||
|
||||
String getFileName();
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
package io.xpipe.core.store;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import lombok.Value;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@Value
|
||||
@JsonTypeName("httpRequest")
|
||||
public class HttpRequestStore implements StreamDataStore {
|
||||
|
||||
public static boolean isHttpRequest(String s) {
|
||||
return s.startsWith("http:") || s.startsWith("https:");
|
||||
}
|
||||
|
||||
public static Optional<HttpRequestStore> fromString(String s) {
|
||||
try {
|
||||
var uri = new URI(s);
|
||||
return Optional.of(new HttpRequestStore(uri, Map.of()));
|
||||
} catch (URISyntaxException e) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
URI uri;
|
||||
Map<String, String> headers;
|
||||
|
||||
@Override
|
||||
public InputStream openInput() throws Exception {
|
||||
var b = HttpRequest.newBuilder().uri(uri);
|
||||
headers.forEach(b::setHeader);
|
||||
var req = b.GET().build();
|
||||
|
||||
var client = HttpClient.newHttpClient();
|
||||
var res = client.send(req, HttpResponse.BodyHandlers.ofByteArray());
|
||||
|
||||
return new ByteArrayInputStream(res.body());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists() {
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
package io.xpipe.core.store;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
|
||||
@JsonTypeName("local")
|
||||
@EqualsAndHashCode
|
||||
public class LocalFileDataStore implements FileDataStore {
|
||||
|
||||
private final Path file;
|
||||
|
||||
@JsonCreator
|
||||
public LocalFileDataStore(Path file) {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return getFileName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Instant> determineLastModified() {
|
||||
try {
|
||||
var l = Files.getLastModifiedTime(file);
|
||||
return Optional.of(l.toInstant());
|
||||
} catch (IOException e) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
public Path getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream openInput() throws Exception {
|
||||
return new BufferedInputStream(Files.newInputStream(file));
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream openOutput() throws Exception {
|
||||
return Files.newOutputStream(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream openAppendingOutput() throws Exception {
|
||||
return Files.newOutputStream(file, StandardOpenOption.APPEND);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists() {
|
||||
return Files.exists(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFileName() {
|
||||
return file.getFileName().toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package io.xpipe.core.store;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
@JsonTypeName("local")
|
||||
public class LocalMachineStore implements MachineStore {
|
||||
|
||||
@Override
|
||||
public String toDisplay() {
|
||||
return "local";
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream openInput(String file) throws Exception {
|
||||
var p = Path.of(file);
|
||||
return Files.newInputStream(p);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream openOutput(String file) throws Exception {
|
||||
var p = Path.of(file);
|
||||
return Files.newOutputStream(p);
|
||||
}
|
||||
}
|
16
core/src/main/java/io/xpipe/core/store/MachineStore.java
Normal file
16
core/src/main/java/io/xpipe/core/store/MachineStore.java
Normal file
|
@ -0,0 +1,16 @@
|
|||
package io.xpipe.core.store;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public interface MachineStore extends DataStore {
|
||||
|
||||
static MachineStore local() {
|
||||
return new LocalMachineStore();
|
||||
}
|
||||
|
||||
InputStream openInput(String file) throws Exception;
|
||||
|
||||
OutputStream openOutput(String file) throws Exception;
|
||||
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package io.xpipe.core.store;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
|
||||
public class RemoteFileDataStore implements StreamDataStore {
|
||||
|
||||
@Override
|
||||
public Optional<String> determineDefaultName() {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Instant> determineLastModified() {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream openInput() throws Exception {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream openOutput() throws Exception {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists() {
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -1,14 +1,7 @@
|
|||
package io.xpipe.core.store;
|
||||
|
||||
import lombok.NonNull;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* A data store that can be accessed using InputStreams and/or OutputStreams.
|
||||
|
@ -16,21 +9,6 @@ import java.util.Optional;
|
|||
*/
|
||||
public interface StreamDataStore extends DataStore {
|
||||
|
||||
static Optional<StreamDataStore> fromString(@NonNull String s) {
|
||||
try {
|
||||
var path = Path.of(s);
|
||||
return Optional.of(new LocalFileDataStore(path));
|
||||
} catch (InvalidPathException ignored) {
|
||||
}
|
||||
|
||||
try {
|
||||
var path = new URL(s);
|
||||
} catch (MalformedURLException ignored) {
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens an input stream. This input stream does not necessarily have to be a new instance.
|
||||
*/
|
||||
|
|
|
@ -11,19 +11,20 @@ import com.fasterxml.jackson.databind.SerializerProvider;
|
|||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.jsontype.NamedType;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import io.xpipe.core.dialog.BaseQueryElement;
|
||||
import io.xpipe.core.dialog.ChoiceElement;
|
||||
import io.xpipe.core.dialog.HeaderElement;
|
||||
import io.xpipe.core.data.type.ArrayType;
|
||||
import io.xpipe.core.data.type.TupleType;
|
||||
import io.xpipe.core.data.type.ValueType;
|
||||
import io.xpipe.core.data.type.WildcardType;
|
||||
import io.xpipe.core.dialog.BaseQueryElement;
|
||||
import io.xpipe.core.dialog.BusyElement;
|
||||
import io.xpipe.core.dialog.ChoiceElement;
|
||||
import io.xpipe.core.dialog.HeaderElement;
|
||||
import io.xpipe.core.source.DataSourceInfo;
|
||||
import io.xpipe.core.source.DataSourceReference;
|
||||
import io.xpipe.core.store.CollectionEntryDataStore;
|
||||
import io.xpipe.core.store.HttpRequestStore;
|
||||
import io.xpipe.core.store.FileStore;
|
||||
import io.xpipe.core.store.LocalDirectoryDataStore;
|
||||
import io.xpipe.core.store.LocalFileDataStore;
|
||||
import io.xpipe.core.store.LocalMachineStore;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
|
@ -34,10 +35,9 @@ public class CoreJacksonModule extends SimpleModule {
|
|||
@Override
|
||||
public void setupModule(SetupContext context) {
|
||||
context.registerSubtypes(
|
||||
new NamedType(LocalFileDataStore.class),
|
||||
new NamedType(FileStore.class),
|
||||
new NamedType(LocalDirectoryDataStore.class),
|
||||
new NamedType(CollectionEntryDataStore.class),
|
||||
new NamedType(HttpRequestStore.class),
|
||||
new NamedType(ValueType.class),
|
||||
new NamedType(TupleType.class),
|
||||
new NamedType(ArrayType.class),
|
||||
|
@ -49,6 +49,8 @@ public class CoreJacksonModule extends SimpleModule {
|
|||
new NamedType(DataSourceInfo.Raw.class),
|
||||
new NamedType(BaseQueryElement.class),
|
||||
new NamedType(ChoiceElement.class),
|
||||
new NamedType(BusyElement.class),
|
||||
new NamedType(LocalMachineStore.class),
|
||||
new NamedType(HeaderElement.class)
|
||||
);
|
||||
|
||||
|
@ -58,6 +60,9 @@ public class CoreJacksonModule extends SimpleModule {
|
|||
addSerializer(Path.class, new LocalPathSerializer());
|
||||
addDeserializer(Path.class, new LocalPathDeserializer());
|
||||
|
||||
addSerializer(Secret.class, new SecretSerializer());
|
||||
addDeserializer(Secret.class, new SecretDeserializer());
|
||||
|
||||
addSerializer(DataSourceReference.class, new DataSourceReferenceSerializer());
|
||||
addDeserializer(DataSourceReference.class, new DataSourceReferenceDeserializer());
|
||||
|
||||
|
@ -119,6 +124,23 @@ public class CoreJacksonModule extends SimpleModule {
|
|||
}
|
||||
}
|
||||
|
||||
public static class SecretSerializer extends JsonSerializer<Secret> {
|
||||
|
||||
@Override
|
||||
public void serialize(Secret value, JsonGenerator jgen, SerializerProvider provider)
|
||||
throws IOException {
|
||||
jgen.writeString(value.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public static class SecretDeserializer extends JsonDeserializer<Secret> {
|
||||
|
||||
@Override
|
||||
public Secret deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
|
||||
return Secret.parse(p.getValueAsString());
|
||||
}
|
||||
}
|
||||
|
||||
@JsonSerialize(as = Throwable.class)
|
||||
public abstract static class ThrowableTypeMixIn {
|
||||
|
||||
|
|
30
core/src/main/java/io/xpipe/core/util/Secret.java
Normal file
30
core/src/main/java/io/xpipe/core/util/Secret.java
Normal file
|
@ -0,0 +1,30 @@
|
|||
package io.xpipe.core.util;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode
|
||||
public class Secret {
|
||||
|
||||
public static Secret parse(String s) {
|
||||
return new Secret(Base64.getEncoder().encodeToString(s.getBytes(StandardCharsets.UTF_8)));
|
||||
}
|
||||
|
||||
String value;
|
||||
|
||||
public String getDisplay() {
|
||||
return "*".repeat(value.length());
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public String getSecret() {
|
||||
return new String(Base64.getDecoder().decode(value), StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
package io.xpipe.extension;
|
||||
|
||||
import io.xpipe.charsetter.NewLine;
|
||||
import io.xpipe.core.dialog.Dialog;
|
||||
import io.xpipe.core.dialog.QueryConverter;
|
||||
import io.xpipe.core.dialog.ConfigParameter;
|
||||
import io.xpipe.core.source.DataSource;
|
||||
import io.xpipe.core.source.DataSourceType;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
|
@ -10,10 +10,10 @@ import javafx.beans.property.Property;
|
|||
import javafx.scene.layout.Region;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
public interface DataSourceProvider<T extends DataSource<?>> {
|
||||
|
||||
|
@ -90,57 +90,27 @@ public interface DataSourceProvider<T extends DataSource<?>> {
|
|||
|
||||
}
|
||||
|
||||
interface ConfigProvider<T extends DataSource<?>> {
|
||||
public static Dialog charset(Charset c) {
|
||||
return Dialog.query("charset", false, false, c, QueryConverter.CHARSET);
|
||||
}
|
||||
|
||||
static <T extends DataSource<?>> ConfigProvider<T> empty(List<String> names, Function<DataStore, T> func) {
|
||||
return new ConfigProvider<>() {
|
||||
@Override
|
||||
public void applyConfig(T source, Map<ConfigParameter, Object> values) {
|
||||
}
|
||||
public static Dialog newLine(NewLine l) {
|
||||
return Dialog.query("newline", false, false, l, NEW_LINE_CONVERTER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<ConfigParameter, Function<T, Object>> toCompleteConfig() {
|
||||
return Map.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<ConfigParameter, Object> toRequiredReadConfig(T desc) {
|
||||
return Map.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getPossibleNames() {
|
||||
return names;
|
||||
}
|
||||
};
|
||||
public static final QueryConverter<NewLine> NEW_LINE_CONVERTER = new QueryConverter<NewLine>() {
|
||||
@Override
|
||||
protected NewLine fromString(String s) {
|
||||
return NewLine.id(s);
|
||||
}
|
||||
|
||||
ConfigParameter CHARSET = new ConfigParameter(
|
||||
"charset", QueryConverter.CHARSET);
|
||||
@Override
|
||||
protected String toString(NewLine value) {
|
||||
return value.getId();
|
||||
}
|
||||
};
|
||||
|
||||
public static final QueryConverter<NewLine> NEW_LINE_CONVERTER = new QueryConverter<NewLine>() {
|
||||
@Override
|
||||
protected NewLine fromString(String s) {
|
||||
return NewLine.id(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String toString(NewLine value) {
|
||||
return value.getId();
|
||||
}
|
||||
};
|
||||
|
||||
ConfigParameter NEWLINE = new ConfigParameter(
|
||||
"newline", NEW_LINE_CONVERTER);
|
||||
|
||||
void applyConfig(T source, Map<ConfigParameter, Object> values);
|
||||
|
||||
Map<ConfigParameter, Function<T, Object>> toCompleteConfig();
|
||||
|
||||
Map<ConfigParameter, Object> toRequiredReadConfig(T desc);
|
||||
|
||||
List<String> getPossibleNames();
|
||||
}
|
||||
Dialog configDialog(T source, boolean all);
|
||||
|
||||
default boolean isHidden() {
|
||||
return false;
|
||||
|
@ -184,12 +154,8 @@ public interface DataSourceProvider<T extends DataSource<?>> {
|
|||
return false;
|
||||
}
|
||||
|
||||
ConfigProvider<T> getConfigProvider();
|
||||
|
||||
default String getId() {
|
||||
var n = getClass().getPackageName();
|
||||
var i = n.lastIndexOf('.');
|
||||
return i != -1 ? n.substring(i + 1) : n;
|
||||
return getPossibleNames().get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -208,4 +174,7 @@ public interface DataSourceProvider<T extends DataSource<?>> {
|
|||
.filter(c -> c.getName().endsWith("Source")).findFirst()
|
||||
.orElseThrow(() -> new ExtensionException("Descriptor class not found for " + getId()));
|
||||
}
|
||||
|
||||
|
||||
List<String> getPossibleNames();
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ package io.xpipe.extension;
|
|||
|
||||
import io.xpipe.core.source.*;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
import io.xpipe.core.store.LocalFileDataStore;
|
||||
import io.xpipe.core.store.FileStore;
|
||||
import io.xpipe.extension.event.ErrorEvent;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
|
@ -46,40 +46,40 @@ public class DataSourceProviders {
|
|||
|
||||
@SuppressWarnings("unchecked")
|
||||
@SneakyThrows
|
||||
public static StructureDataSource<LocalFileDataStore> createLocalStructureDescriptor(DataStore store) {
|
||||
return (StructureDataSource<LocalFileDataStore>)
|
||||
public static StructureDataSource<FileStore> createLocalStructureDescriptor(DataStore store) {
|
||||
return (StructureDataSource<FileStore>)
|
||||
DataSourceProviders.byId("xpbs").getSourceClass()
|
||||
.getDeclaredConstructors()[0].newInstance(store);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@SneakyThrows
|
||||
public static RawDataSource<LocalFileDataStore> createLocalRawDescriptor(DataStore store) {
|
||||
return (RawDataSource<LocalFileDataStore>)
|
||||
public static RawDataSource<FileStore> createLocalRawDescriptor(DataStore store) {
|
||||
return (RawDataSource<FileStore>)
|
||||
DataSourceProviders.byId("binary").getSourceClass()
|
||||
.getDeclaredConstructors()[0].newInstance(store);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@SneakyThrows
|
||||
public static RawDataSource<LocalFileDataStore> createLocalCollectionDescriptor(DataStore store) {
|
||||
return (RawDataSource<LocalFileDataStore>)
|
||||
public static RawDataSource<FileStore> createLocalCollectionDescriptor(DataStore store) {
|
||||
return (RawDataSource<FileStore>)
|
||||
DataSourceProviders.byId("br").getSourceClass()
|
||||
.getDeclaredConstructors()[0].newInstance(store);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@SneakyThrows
|
||||
public static TextDataSource<LocalFileDataStore> createLocalTextDescriptor(DataStore store) {
|
||||
return (TextDataSource<LocalFileDataStore>)
|
||||
public static TextDataSource<FileStore> createLocalTextDescriptor(DataStore store) {
|
||||
return (TextDataSource<FileStore>)
|
||||
DataSourceProviders.byId("text").getSourceClass()
|
||||
.getDeclaredConstructors()[0].newInstance(store);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@SneakyThrows
|
||||
public static TableDataSource<LocalFileDataStore> createLocalTableDescriptor(DataStore store) {
|
||||
return (TableDataSource<LocalFileDataStore>)
|
||||
public static TableDataSource<FileStore> createLocalTableDescriptor(DataStore store) {
|
||||
return (TableDataSource<FileStore>)
|
||||
DataSourceProviders.byId("xpbt").getSourceClass()
|
||||
.getDeclaredConstructors()[0].newInstance(store);
|
||||
}
|
||||
|
@ -110,7 +110,7 @@ public class DataSourceProviders {
|
|||
throw new IllegalStateException("Not initialized");
|
||||
}
|
||||
|
||||
return ALL.stream().filter(d -> d.getConfigProvider().getPossibleNames().stream()
|
||||
return ALL.stream().filter(d -> d.getPossibleNames().stream()
|
||||
.anyMatch(s -> s.equalsIgnoreCase(name))).findAny();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package io.xpipe.extension;
|
||||
|
||||
import io.xpipe.core.dialog.Dialog;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
|
@ -34,13 +35,19 @@ public interface DataStoreProvider {
|
|||
return null;
|
||||
}
|
||||
|
||||
default Dialog dialogForString(String s) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Dialog defaultDialog();
|
||||
|
||||
default String display(DataStore store) {
|
||||
return store.toDisplay();
|
||||
}
|
||||
|
||||
List<String> getPossibleNames();
|
||||
|
||||
default String getId() {
|
||||
var n = getClass().getPackageName();
|
||||
var i = n.lastIndexOf('.');
|
||||
return i != -1 ? n.substring(i + 1) : n;
|
||||
return getPossibleNames().get(0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,6 +45,14 @@ public class DataStoreProviders {
|
|||
return ALL.stream().map(d -> d.dialogForURL(url)).filter(Objects::nonNull).findAny();
|
||||
}
|
||||
|
||||
public static Optional<Dialog> byString(String s) {
|
||||
if (ALL == null) {
|
||||
throw new IllegalStateException("Not initialized");
|
||||
}
|
||||
|
||||
return ALL.stream().map(d -> d.dialogForString(s)).filter(Objects::nonNull).findAny();
|
||||
}
|
||||
|
||||
public static Set<DataStoreProvider> getAll() {
|
||||
return ALL;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ package io.xpipe.extension;
|
|||
|
||||
import io.xpipe.core.source.DataSource;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
import io.xpipe.core.store.FileDataStore;
|
||||
import io.xpipe.core.store.FileStore;
|
||||
import io.xpipe.core.store.StreamDataStore;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
|
@ -23,7 +23,7 @@ public interface SimpleFileDataSourceProvider<T extends DataSource<?>> extends D
|
|||
continue;
|
||||
}
|
||||
|
||||
if (store instanceof FileDataStore l) {
|
||||
if (store instanceof FileStore l) {
|
||||
return l.getFileName().endsWith("." + ext);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
package io.xpipe.extension;
|
||||
|
||||
import io.xpipe.core.dialog.Dialog;
|
||||
import io.xpipe.core.source.DataSource;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.List;
|
||||
|
||||
public interface UniformDataSourceProvider<T extends DataSource<?>> extends DataSourceProvider<T> {
|
||||
|
||||
@Override
|
||||
default ConfigProvider<T> getConfigProvider() {
|
||||
return ConfigProvider.empty(List.of(getId()), this::createDefaultSource);
|
||||
default Dialog configDialog(T source, boolean all) {
|
||||
return Dialog.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -10,5 +10,9 @@ public interface PrefsChoiceValue extends Translatable {
|
|||
return I18n.get(getId());
|
||||
}
|
||||
|
||||
default String toDefaultString() {
|
||||
return I18n.get(getId());
|
||||
}
|
||||
|
||||
String getId();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue