diff --git a/common/pom.xml b/common/pom.xml index 72fff275..99f43063 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -21,4 +21,13 @@ 8 UTF-8 + + + + com.squareup.okhttp3 + mockwebserver + ${okhttp-version} + test + + \ No newline at end of file diff --git a/common/src/main/java/com/skyflow/BaseSkyflow.java b/common/src/main/java/com/skyflow/BaseSkyflow.java index d02dc45a..fdca3f26 100644 --- a/common/src/main/java/com/skyflow/BaseSkyflow.java +++ b/common/src/main/java/com/skyflow/BaseSkyflow.java @@ -4,7 +4,7 @@ import com.skyflow.config.Credentials; import com.skyflow.enums.LogLevel; import com.skyflow.errors.ErrorCode; -import com.skyflow.errors.ErrorMessage; +import com.skyflow.errors.BaseErrorMessage; import com.skyflow.errors.SkyflowException; import com.skyflow.logs.ErrorLogs; import com.skyflow.logs.InfoLogs; @@ -66,7 +66,7 @@ public LogLevel getLogLevel() { } protected static T resolveOrThrow(Map map, String key, - ErrorLogs errorLog, ErrorMessage errorMessage) throws SkyflowException { + ErrorLogs errorLog, BaseErrorMessage errorMessage) throws SkyflowException { T value = key != null ? map.get(key) : map.values().stream().findFirst().orElse(null); if (value == null) { // The log line carries a %s1 placeholder for the id. Callers that resolve the single @@ -125,7 +125,7 @@ protected final void addVaultConfigTemplate(V vaultConfig) throws SkyflowExcepti ErrorLogs.VAULT_CONFIG_EXISTS.getLog(), vaultId )); throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), - ErrorMessage.VaultIdAlreadyInConfigList.getMessage()); + BaseErrorMessage.VaultIdAlreadyInConfigList.getMessage()); } onVaultConfigAdded(vaultConfigCopy); this.vaultConfigMap.put(vaultId, vaultConfigCopy); @@ -139,7 +139,7 @@ protected final void updateVaultConfigTemplate(V vaultConfig) throws SkyflowExce LogUtil.printErrorLog(BaseUtils.parameterizedString( ErrorLogs.VAULT_CONFIG_DOES_NOT_EXIST.getLog(), vaultId )); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.VaultIdNotInConfigList.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.VaultIdNotInConfigList.getMessage()); } V previousConfig = this.vaultConfigMap.get(vaultId); V merged = mergeVaultConfig(vaultConfig, cloneVaultConfig(previousConfig)); @@ -150,7 +150,7 @@ protected final void updateVaultConfigTemplate(V vaultConfig) throws SkyflowExce protected final void removeVaultConfigTemplate(String vaultId) throws SkyflowException { if (!hasVaultClient(vaultId)) { LogUtil.printErrorLog(BaseUtils.parameterizedString(ErrorLogs.VAULT_CONFIG_DOES_NOT_EXIST.getLog(), vaultId)); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.VaultIdNotInConfigList.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.VaultIdNotInConfigList.getMessage()); } onVaultConfigRemoved(vaultId); this.vaultConfigMap.remove(vaultId); diff --git a/common/src/main/java/com/skyflow/BaseVaultClient.java b/common/src/main/java/com/skyflow/BaseVaultClient.java index 3d5b0d32..973510d7 100644 --- a/common/src/main/java/com/skyflow/BaseVaultClient.java +++ b/common/src/main/java/com/skyflow/BaseVaultClient.java @@ -5,7 +5,7 @@ import com.skyflow.config.BaseCredentials; import com.skyflow.config.BaseVaultConfig; import com.skyflow.errors.ErrorCode; -import com.skyflow.errors.ErrorMessage; +import com.skyflow.errors.BaseErrorMessage; import com.skyflow.errors.SkyflowException; import com.skyflow.logs.ErrorLogs; import com.skyflow.logs.InfoLogs; @@ -25,7 +25,7 @@ import java.util.concurrent.TimeUnit; import java.util.function.Supplier; -class BaseVaultClient { +public class BaseVaultClient { protected V vaultConfig; protected OkHttpClient sharedHttpClient; protected String currentVaultURL; @@ -69,7 +69,7 @@ protected synchronized void prioritiseCredentials(BaseCredentials vaultSpecificC sysCredentials = dotenv.get(BaseConstants.ENV_CREDENTIALS_KEY_NAME); } if (sysCredentials == null) { - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyCredentials.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.EmptyCredentials.getMessage()); } else { this.finalCredentials = new BaseCredentials(); this.finalCredentials.setCredentialsString(sysCredentials); @@ -80,7 +80,7 @@ protected synchronized void prioritiseCredentials(BaseCredentials vaultSpecificC apiKey = null; } } catch (DotenvException e) { - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyCredentials.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.EmptyCredentials.getMessage()); } catch (SkyflowException e) { throw e; } catch (Exception e) { diff --git a/common/src/main/java/com/skyflow/detect/Audio.java b/common/src/main/java/com/skyflow/detect/Audio.java new file mode 100644 index 00000000..6d4e46f9 --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/Audio.java @@ -0,0 +1,93 @@ +package com.skyflow.detect; + +/** + * Audio processing options. + */ +public final class Audio { + private final Boolean outputProcessedAudio; + private final OutputTranscriptionType outputTranscription; + private final Bleep bleep; + + private Audio(AudioBuilder builder) { + this.outputProcessedAudio = builder.outputProcessedAudio; + this.outputTranscription = builder.outputTranscription; + this.bleep = builder.bleep; + } + + /** + * @return a new builder + */ + public static AudioBuilder builder() { + return new AudioBuilder(); + } + + /** + * @return Whether to return the processed audio file. + */ + public Boolean getOutputProcessedAudio() { + return outputProcessedAudio; + } + + /** + * @return Transcription output to produce. + */ + public OutputTranscriptionType getOutputTranscription() { + return outputTranscription; + } + + /** + * @return Bleep configuration. + */ + public Bleep getBleep() { + return bleep; + } + + @Override + public String toString() { + return "Audio{" + + "outputProcessedAudio=" + outputProcessedAudio + + ", outputTranscription=" + outputTranscription + + ", bleep=" + bleep + + '}'; + } + + public static final class AudioBuilder { + private Boolean outputProcessedAudio; + private OutputTranscriptionType outputTranscription; + private Bleep bleep; + + private AudioBuilder() { + } + + /** + * @param outputProcessedAudio Whether to return the processed audio file. + * @return this builder + */ + public AudioBuilder outputProcessedAudio(Boolean outputProcessedAudio) { + this.outputProcessedAudio = outputProcessedAudio; + return this; + } + + /** + * @param outputTranscription Transcription output to produce. + * @return this builder + */ + public AudioBuilder outputTranscription(OutputTranscriptionType outputTranscription) { + this.outputTranscription = outputTranscription; + return this; + } + + /** + * @param bleep Bleep configuration. + * @return this builder + */ + public AudioBuilder bleep(Bleep bleep) { + this.bleep = bleep; + return this; + } + + public Audio build() { + return new Audio(this); + } + } +} diff --git a/common/src/main/java/com/skyflow/detect/Bleep.java b/common/src/main/java/com/skyflow/detect/Bleep.java new file mode 100644 index 00000000..63346aeb --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/Bleep.java @@ -0,0 +1,113 @@ +package com.skyflow.detect; + +/** + * Bleep tone applied over detected speech in audio. + */ +public final class Bleep { + private final Float startPadding; + private final Float stopPadding; + private final Integer frequency; + private final Integer gain; + + private Bleep(BleepBuilder builder) { + this.startPadding = builder.startPadding; + this.stopPadding = builder.stopPadding; + this.frequency = builder.frequency; + this.gain = builder.gain; + } + + /** + * @return a new builder + */ + public static BleepBuilder builder() { + return new BleepBuilder(); + } + + /** + * @return Seconds of bleep added before the detected span. + */ + public Float getStartPadding() { + return startPadding; + } + + /** + * @return Seconds of bleep added after the detected span. + */ + public Float getStopPadding() { + return stopPadding; + } + + /** + * @return Bleep frequency in Hz. + */ + public Integer getFrequency() { + return frequency; + } + + /** + * @return Bleep gain. + */ + public Integer getGain() { + return gain; + } + + @Override + public String toString() { + return "Bleep{" + + "startPadding=" + startPadding + + ", stopPadding=" + stopPadding + + ", frequency=" + frequency + + ", gain=" + gain + + '}'; + } + + public static final class BleepBuilder { + private Float startPadding; + private Float stopPadding; + private Integer frequency; + private Integer gain; + + private BleepBuilder() { + } + + /** + * @param startPadding Seconds of bleep added before the detected span. + * @return this builder + */ + public BleepBuilder startPadding(Float startPadding) { + this.startPadding = startPadding; + return this; + } + + /** + * @param stopPadding Seconds of bleep added after the detected span. + * @return this builder + */ + public BleepBuilder stopPadding(Float stopPadding) { + this.stopPadding = stopPadding; + return this; + } + + /** + * @param frequency Bleep frequency in Hz. + * @return this builder + */ + public BleepBuilder frequency(Integer frequency) { + this.frequency = frequency; + return this; + } + + /** + * @param gain Bleep gain. + * @return this builder + */ + public BleepBuilder gain(Integer gain) { + this.gain = gain; + return this; + } + + public Bleep build() { + return new Bleep(this); + } + } +} diff --git a/common/src/main/java/com/skyflow/detect/CheckGuardrailsRequest.java b/common/src/main/java/com/skyflow/detect/CheckGuardrailsRequest.java new file mode 100644 index 00000000..526ddd9c --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/CheckGuardrailsRequest.java @@ -0,0 +1,95 @@ +package com.skyflow.detect; + +import java.util.List; + +/** + * Request for {@code checkGuardrails}: screens text for toxicity and denied topics. + */ +public final class CheckGuardrailsRequest { + private final String text; + private final Boolean checkToxicity; + private final List denyTopics; + + private CheckGuardrailsRequest(CheckGuardrailsRequestBuilder builder) { + this.text = builder.text; + this.checkToxicity = builder.checkToxicity; + this.denyTopics = builder.denyTopics; + } + + /** + * @return a new builder + */ + public static CheckGuardrailsRequestBuilder builder() { + return new CheckGuardrailsRequestBuilder(); + } + + /** + * @return Text to screen. Required. + */ + public String getText() { + return text; + } + + /** + * @return Whether to check the text for toxicity. + */ + public Boolean getCheckToxicity() { + return checkToxicity; + } + + /** + * @return Topics the text must not discuss. + */ + public List getDenyTopics() { + return denyTopics; + } + + @Override + public String toString() { + return "CheckGuardrailsRequest{" + + "text=" + text + + ", checkToxicity=" + checkToxicity + + ", denyTopics=" + denyTopics + + '}'; + } + + public static final class CheckGuardrailsRequestBuilder { + private String text; + private Boolean checkToxicity; + private List denyTopics; + + private CheckGuardrailsRequestBuilder() { + } + + /** + * @param text Text to screen. Required. + * @return this builder + */ + public CheckGuardrailsRequestBuilder text(String text) { + this.text = text; + return this; + } + + /** + * @param checkToxicity Whether to check the text for toxicity. + * @return this builder + */ + public CheckGuardrailsRequestBuilder checkToxicity(Boolean checkToxicity) { + this.checkToxicity = checkToxicity; + return this; + } + + /** + * @param denyTopics Topics the text must not discuss. + * @return this builder + */ + public CheckGuardrailsRequestBuilder denyTopics(List denyTopics) { + this.denyTopics = denyTopics; + return this; + } + + public CheckGuardrailsRequest build() { + return new CheckGuardrailsRequest(this); + } + } +} diff --git a/common/src/main/java/com/skyflow/detect/CheckGuardrailsResponse.java b/common/src/main/java/com/skyflow/detect/CheckGuardrailsResponse.java new file mode 100644 index 00000000..153e68cf --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/CheckGuardrailsResponse.java @@ -0,0 +1,56 @@ +package com.skyflow.detect; + +/** + * Response of {@code checkGuardrails}. + */ +public final class CheckGuardrailsResponse { + private final String text; + private final GuardrailsValidation validation; + private final Boolean toxic; + private final Boolean deniedTopic; + + public CheckGuardrailsResponse(String text, GuardrailsValidation validation, Boolean toxic, Boolean deniedTopic) { + this.text = text; + this.validation = validation; + this.toxic = toxic; + this.deniedTopic = deniedTopic; + } + + /** + * @return The text that was screened. + */ + public String getText() { + return text; + } + + /** + * @return PASSED when no guardrail was triggered, FAILED otherwise. + */ + public GuardrailsValidation getValidation() { + return validation; + } + + /** + * @return Whether the text was judged toxic. Null when toxicity was not checked. + */ + public Boolean getToxic() { + return toxic; + } + + /** + * @return Whether the text touched a denied topic. Null when no topics were supplied. + */ + public Boolean getDeniedTopic() { + return deniedTopic; + } + + @Override + public String toString() { + return "CheckGuardrailsResponse{" + + "text=" + text + + ", validation=" + validation + + ", toxic=" + toxic + + ", deniedTopic=" + deniedTopic + + '}'; + } +} diff --git a/common/src/main/java/com/skyflow/detect/CustomType.java b/common/src/main/java/com/skyflow/detect/CustomType.java new file mode 100644 index 00000000..1eee269a --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/CustomType.java @@ -0,0 +1,75 @@ +package com.skyflow.detect; + +import java.util.List; + +/** + * A custom, regex-defined entity type. + */ +public final class CustomType { + private final String label; + private final List match; + + private CustomType(CustomTypeBuilder builder) { + this.label = builder.label; + this.match = builder.match; + } + + /** + * @return a new builder + */ + public static CustomTypeBuilder builder() { + return new CustomTypeBuilder(); + } + + /** + * @return Label reported as the entityType of matches. + */ + public String getLabel() { + return label; + } + + /** + * @return Regular expressions that identify the custom entity. + */ + public List getMatch() { + return match; + } + + @Override + public String toString() { + return "CustomType{" + + "label=" + label + + ", match=" + match + + '}'; + } + + public static final class CustomTypeBuilder { + private String label; + private List match; + + private CustomTypeBuilder() { + } + + /** + * @param label Label reported as the entityType of matches. + * @return this builder + */ + public CustomTypeBuilder label(String label) { + this.label = label; + return this; + } + + /** + * @param match Regular expressions that identify the custom entity. + * @return this builder + */ + public CustomTypeBuilder match(List match) { + this.match = match; + return this; + } + + public CustomType build() { + return new CustomType(this); + } + } +} diff --git a/common/src/main/java/com/skyflow/detect/DataSourceType.java b/common/src/main/java/com/skyflow/detect/DataSourceType.java new file mode 100644 index 00000000..d152fbb4 --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/DataSourceType.java @@ -0,0 +1,10 @@ +package com.skyflow.detect; + +/** + * How file content is supplied to, or returned by, Detect file operations. + */ +public enum DataSourceType { + BASE64, + SKYFLOW_ID, + PRESIGNED_URL; +} diff --git a/common/src/main/java/com/skyflow/detect/DeidentificationType.java b/common/src/main/java/com/skyflow/detect/DeidentificationType.java new file mode 100644 index 00000000..067d533f --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/DeidentificationType.java @@ -0,0 +1,12 @@ +package com.skyflow.detect; + +/** + * How a detected entity is replaced in the processed output. + */ +public enum DeidentificationType { + UNKNOWN, + ENTITY_UNIQUE_COUNTER, + ENTITY_ONLY, + VAULT_TOKEN, + MASK; +} diff --git a/common/src/main/java/com/skyflow/detect/DeidentifyFileRequest.java b/common/src/main/java/com/skyflow/detect/DeidentifyFileRequest.java new file mode 100644 index 00000000..b91b3a76 --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/DeidentifyFileRequest.java @@ -0,0 +1,212 @@ +package com.skyflow.detect; + +import java.io.File; + +/** + * Request for {@code deidentifyFile}. Supply the file through exactly one of {@code value} (with a + * {@code dataSource}), {@code file} or {@code filePath}. The two file forms are builder sugar that set + * {@code dataSource = BASE64}, encode the content into {@code value} when the request is sent, and infer + * {@code dataFormat} from the extension unless it is set explicitly. + * + *

Without {@code pollOptions} the call submits the run and returns its id. With {@code pollOptions} the + * SDK polls the run and, on success for a file-based request, writes the outputs to disk. + */ +public final class DeidentifyFileRequest { + private final DataSourceType dataSource; + private final String value; + private final File file; + private final FileDataFormat dataFormat; + private final String configurationId; + private final DetectConfiguration configuration; + private final PollOptions pollOptions; + private final String outputDirectory; + + private DeidentifyFileRequest(DeidentifyFileRequestBuilder builder) { + this.dataSource = builder.dataSource; + this.value = builder.value; + this.file = builder.file; + this.dataFormat = builder.dataFormat; + this.configurationId = builder.configurationId; + this.configuration = builder.configuration; + this.pollOptions = builder.pollOptions; + this.outputDirectory = builder.outputDirectory; + } + + /** + * @return a new builder + */ + public static DeidentifyFileRequestBuilder builder() { + return new DeidentifyFileRequestBuilder(); + } + + /** + * @return How {@code value} supplies the file. Required unless {@code file} or {@code filePath} was used. + */ + public DataSourceType getDataSource() { + return dataSource; + } + + /** + * @return Base64 content, skyflow id or presigned URL, according to {@code dataSource}. + */ + public String getValue() { + return value; + } + + /** + * @return Local file to de-identify, when {@code file} or {@code filePath} was used. + */ + public File getFile() { + return file; + } + + /** + * @return Format of the file. Optional; inferred from the file extension or by the server when omitted. + */ + public FileDataFormat getDataFormat() { + return dataFormat; + } + + /** + * @return Id of a stored Detect configuration. + */ + public String getConfigurationId() { + return configurationId; + } + + /** + * @return Inline Detect configuration. + */ + public DetectConfiguration getConfiguration() { + return configuration; + } + + /** + * @return Polling settings, or null to submit only. + */ + public PollOptions getPollOptions() { + return pollOptions; + } + + /** + * @return Directory the outputs are written to on success. Only with {@code pollOptions} and a file-based request. + */ + public String getOutputDirectory() { + return outputDirectory; + } + + @Override + public String toString() { + return "DeidentifyFileRequest{" + + "dataSource=" + dataSource + + ", value=" + (value == null ? null : "<" + value.length() + " chars>") + + ", file=" + file + + ", dataFormat=" + dataFormat + + ", configurationId=" + configurationId + + ", configuration=" + configuration + + ", pollOptions=" + pollOptions + + ", outputDirectory=" + outputDirectory + + '}'; + } + + public static final class DeidentifyFileRequestBuilder { + private DataSourceType dataSource; + private String value; + private File file; + private FileDataFormat dataFormat; + private String configurationId; + private DetectConfiguration configuration; + private PollOptions pollOptions; + private String outputDirectory; + + private DeidentifyFileRequestBuilder() { + } + + /** + * @param dataSource How {@code value} supplies the file. + * @return this builder + */ + public DeidentifyFileRequestBuilder dataSource(DataSourceType dataSource) { + this.dataSource = dataSource; + return this; + } + + /** + * @param value Base64 content, skyflow id or presigned URL, according to {@code dataSource}. + * @return this builder + */ + public DeidentifyFileRequestBuilder value(String value) { + this.value = value; + return this; + } + + /** + * @param file Local file to de-identify. Sets {@code dataSource} to BASE64; the content is encoded when sent. + * @return this builder + */ + public DeidentifyFileRequestBuilder file(File file) { + this.file = file; + if (file != null) { + this.dataSource = DataSourceType.BASE64; + } + return this; + } + + /** + * @param filePath Path of a local file to de-identify. Same as {@link #file(File)}. + * @return this builder + */ + public DeidentifyFileRequestBuilder filePath(String filePath) { + return file(filePath == null ? null : new File(filePath)); + } + + /** + * @param dataFormat Format of the file. Optional. + * @return this builder + */ + public DeidentifyFileRequestBuilder dataFormat(FileDataFormat dataFormat) { + this.dataFormat = dataFormat; + return this; + } + + /** + * @param configurationId Id of a stored Detect configuration. + * @return this builder + */ + public DeidentifyFileRequestBuilder configurationId(String configurationId) { + this.configurationId = configurationId; + return this; + } + + /** + * @param configuration Inline Detect configuration. + * @return this builder + */ + public DeidentifyFileRequestBuilder configuration(DetectConfiguration configuration) { + this.configuration = configuration; + return this; + } + + /** + * @param pollOptions Poll the run after submitting. Null to submit only. + * @return this builder + */ + public DeidentifyFileRequestBuilder pollOptions(PollOptions pollOptions) { + this.pollOptions = pollOptions; + return this; + } + + /** + * @param outputDirectory Where to write outputs on success. Requires {@code pollOptions} and a file-based request. + * @return this builder + */ + public DeidentifyFileRequestBuilder outputDirectory(String outputDirectory) { + this.outputDirectory = outputDirectory; + return this; + } + + public DeidentifyFileRequest build() { + return new DeidentifyFileRequest(this); + } + } +} diff --git a/common/src/main/java/com/skyflow/detect/DeidentifyFileResponse.java b/common/src/main/java/com/skyflow/detect/DeidentifyFileResponse.java new file mode 100644 index 00000000..482b3b63 --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/DeidentifyFileResponse.java @@ -0,0 +1,83 @@ +package com.skyflow.detect; + +import java.util.List; + +/** + * Response of {@code deidentifyFile}. {@code runId} is always present. {@code status} is null when the + * request did not poll; retrieve the run later with {@code getRun}. When polled, the remaining fields + * mirror {@link GetRunResponse}: output, outputType and metrics are populated only on SUCCESS, and + * message carries the failure detail on FAILED. + */ +public final class DeidentifyFileResponse { + private final String runId; + private final DetectRunStatus status; + private final DataSourceType outputType; + private final List output; + private final Metrics metrics; + private final String message; + + public DeidentifyFileResponse(String runId, DetectRunStatus status, DataSourceType outputType, + List output, Metrics metrics, String message) { + this.runId = runId; + this.status = status; + this.outputType = outputType; + this.output = output; + this.metrics = metrics; + this.message = message; + } + + /** + * @return Run id returned by the submit call. Use it with {@code getRun} to retrieve or resume. + */ + public String getRunId() { + return runId; + } + + /** + * @return Run status after polling. Null when the request did not poll. IN_PROGRESS or QUEUED when the + * polling budget ran out before the run finished. + */ + public DetectRunStatus getStatus() { + return status; + } + + /** + * @return How output files are delivered. Null unless polled to SUCCESS. + */ + public DataSourceType getOutputType() { + return outputType; + } + + /** + * @return Output artifacts. Empty unless polled to SUCCESS, never null. + */ + public List getOutput() { + return output; + } + + /** + * @return Processing metrics. Null unless polled to SUCCESS. + */ + public Metrics getMetrics() { + return metrics; + } + + /** + * @return Failure detail when status is FAILED. + */ + public String getMessage() { + return message; + } + + @Override + public String toString() { + return "DeidentifyFileResponse{" + + "runId=" + runId + + ", status=" + status + + ", outputType=" + outputType + + ", output=" + output + + ", metrics=" + metrics + + ", message=" + message + + '}'; + } +} diff --git a/common/src/main/java/com/skyflow/detect/DeidentifyStringRequest.java b/common/src/main/java/com/skyflow/detect/DeidentifyStringRequest.java new file mode 100644 index 00000000..bde84343 --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/DeidentifyStringRequest.java @@ -0,0 +1,94 @@ +package com.skyflow.detect; + +/** + * Request for {@code deidentifyString}. configurationId and configuration are both optional and are sent + * exactly as supplied; the server applies its own rules for their combination. + */ +public final class DeidentifyStringRequest { + private final String text; + private final String configurationId; + private final DetectConfiguration configuration; + + private DeidentifyStringRequest(DeidentifyStringRequestBuilder builder) { + this.text = builder.text; + this.configurationId = builder.configurationId; + this.configuration = builder.configuration; + } + + /** + * @return a new builder + */ + public static DeidentifyStringRequestBuilder builder() { + return new DeidentifyStringRequestBuilder(); + } + + /** + * @return Text to de-identify. Required. + */ + public String getText() { + return text; + } + + /** + * @return Id of a stored Detect configuration. + */ + public String getConfigurationId() { + return configurationId; + } + + /** + * @return Inline Detect configuration. + */ + public DetectConfiguration getConfiguration() { + return configuration; + } + + @Override + public String toString() { + return "DeidentifyStringRequest{" + + "text=" + text + + ", configurationId=" + configurationId + + ", configuration=" + configuration + + '}'; + } + + public static final class DeidentifyStringRequestBuilder { + private String text; + private String configurationId; + private DetectConfiguration configuration; + + private DeidentifyStringRequestBuilder() { + } + + /** + * @param text Text to de-identify. Required. + * @return this builder + */ + public DeidentifyStringRequestBuilder text(String text) { + this.text = text; + return this; + } + + /** + * @param configurationId Id of a stored Detect configuration. + * @return this builder + */ + public DeidentifyStringRequestBuilder configurationId(String configurationId) { + this.configurationId = configurationId; + return this; + } + + /** + * @param configuration Inline Detect configuration. + * @return this builder + */ + public DeidentifyStringRequestBuilder configuration(DetectConfiguration configuration) { + this.configuration = configuration; + return this; + } + + public DeidentifyStringRequest build() { + return new DeidentifyStringRequest(this); + } + } +} diff --git a/common/src/main/java/com/skyflow/detect/DeidentifyStringResponse.java b/common/src/main/java/com/skyflow/detect/DeidentifyStringResponse.java new file mode 100644 index 00000000..7ea68dcf --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/DeidentifyStringResponse.java @@ -0,0 +1,48 @@ +package com.skyflow.detect; + +import java.util.List; + +/** + * Response of {@code deidentifyString}. + */ +public final class DeidentifyStringResponse { + private final String processedText; + private final List entities; + private final Metrics metrics; + + public DeidentifyStringResponse(String processedText, List entities, Metrics metrics) { + this.processedText = processedText; + this.entities = entities; + this.metrics = metrics; + } + + /** + * @return De-identified text. + */ + public String getProcessedText() { + return processedText; + } + + /** + * @return Detected entities. Never null. + */ + public List getEntities() { + return entities; + } + + /** + * @return Processing metrics. + */ + public Metrics getMetrics() { + return metrics; + } + + @Override + public String toString() { + return "DeidentifyStringResponse{" + + "processedText=" + processedText + + ", entities=" + entities + + ", metrics=" + metrics + + '}'; + } +} diff --git a/common/src/main/java/com/skyflow/detect/Detect.java b/common/src/main/java/com/skyflow/detect/Detect.java new file mode 100644 index 00000000..dc998e57 --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/Detect.java @@ -0,0 +1,155 @@ +package com.skyflow.detect; + +import java.util.List; + +/** + * Detection policy: which entities to look for and how to replace them. + */ +public final class Detect { + private final List entities; + private final List objectEntities; + private final List restrict; + private final List skip; + private final List customTypes; + private final ReturnEntitiesType returnEntities; + + private Detect(DetectBuilder builder) { + this.entities = builder.entities; + this.objectEntities = builder.objectEntities; + this.restrict = builder.restrict; + this.skip = builder.skip; + this.customTypes = builder.customTypes; + this.returnEntities = builder.returnEntities; + } + + /** + * @return a new builder + */ + public static DetectBuilder builder() { + return new DetectBuilder(); + } + + /** + * @return Per-entity rules. + */ + public List getEntities() { + return entities; + } + + /** + * @return Per-object rules. File inputs only. + */ + public List getObjectEntities() { + return objectEntities; + } + + /** + * @return Regular expressions that are always treated as sensitive. + */ + public List getRestrict() { + return restrict; + } + + /** + * @return Regular expressions that are never treated as sensitive. + */ + public List getSkip() { + return skip; + } + + /** + * @return Custom regex-defined entity types. + */ + public List getCustomTypes() { + return customTypes; + } + + /** + * @return Which entities to return. Defaults to EXCLUDE_SENSITIVE_DATA on the server. + */ + public ReturnEntitiesType getReturnEntities() { + return returnEntities; + } + + @Override + public String toString() { + return "Detect{" + + "entities=" + entities + + ", objectEntities=" + objectEntities + + ", restrict=" + restrict + + ", skip=" + skip + + ", customTypes=" + customTypes + + ", returnEntities=" + returnEntities + + '}'; + } + + public static final class DetectBuilder { + private List entities; + private List objectEntities; + private List restrict; + private List skip; + private List customTypes; + private ReturnEntitiesType returnEntities; + + private DetectBuilder() { + } + + /** + * @param entities Per-entity rules. + * @return this builder + */ + public DetectBuilder entities(List entities) { + this.entities = entities; + return this; + } + + /** + * @param objectEntities Per-object rules. File inputs only. + * @return this builder + */ + public DetectBuilder objectEntities(List objectEntities) { + this.objectEntities = objectEntities; + return this; + } + + /** + * @param restrict Regular expressions that are always treated as sensitive. + * @return this builder + */ + public DetectBuilder restrict(List restrict) { + this.restrict = restrict; + return this; + } + + /** + * @param skip Regular expressions that are never treated as sensitive. + * @return this builder + */ + public DetectBuilder skip(List skip) { + this.skip = skip; + return this; + } + + /** + * @param customTypes Custom regex-defined entity types. + * @return this builder + */ + public DetectBuilder customTypes(List customTypes) { + this.customTypes = customTypes; + return this; + } + + /** + * @param returnEntities Which entities to return. Defaults to EXCLUDE_SENSITIVE_DATA on the server. + * @return this builder + */ + public DetectBuilder returnEntities(ReturnEntitiesType returnEntities) { + this.returnEntities = returnEntities; + return this; + } + + public Detect build() { + return new Detect(this); + } + } +} diff --git a/common/src/main/java/com/skyflow/detect/DetectConfiguration.java b/common/src/main/java/com/skyflow/detect/DetectConfiguration.java new file mode 100644 index 00000000..e9209528 --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/DetectConfiguration.java @@ -0,0 +1,135 @@ +package com.skyflow.detect; + +import java.util.List; + +/** + * An inline Detect configuration, used instead of a stored configuration id. + */ +public final class DetectConfiguration { + private final String name; + private final String description; + private final Detect detect; + private final List fileMapping; + private final Media media; + + private DetectConfiguration(DetectConfigurationBuilder builder) { + this.name = builder.name; + this.description = builder.description; + this.detect = builder.detect; + this.fileMapping = builder.fileMapping; + this.media = builder.media; + } + + /** + * @return a new builder + */ + public static DetectConfigurationBuilder builder() { + return new DetectConfigurationBuilder(); + } + + /** + * @return Configuration name. + */ + public String getName() { + return name; + } + + /** + * @return Configuration description. + */ + public String getDescription() { + return description; + } + + /** + * @return Detection policy. + */ + public Detect getDetect() { + return detect; + } + + /** + * @return File mappings. File inputs only. + */ + public List getFileMapping() { + return fileMapping; + } + + /** + * @return Media options. File inputs only. + */ + public Media getMedia() { + return media; + } + + @Override + public String toString() { + return "DetectConfiguration{" + + "name=" + name + + ", description=" + description + + ", detect=" + detect + + ", fileMapping=" + fileMapping + + ", media=" + media + + '}'; + } + + public static final class DetectConfigurationBuilder { + private String name; + private String description; + private Detect detect; + private List fileMapping; + private Media media; + + private DetectConfigurationBuilder() { + } + + /** + * @param name Configuration name. + * @return this builder + */ + public DetectConfigurationBuilder name(String name) { + this.name = name; + return this; + } + + /** + * @param description Configuration description. + * @return this builder + */ + public DetectConfigurationBuilder description(String description) { + this.description = description; + return this; + } + + /** + * @param detect Detection policy. + * @return this builder + */ + public DetectConfigurationBuilder detect(Detect detect) { + this.detect = detect; + return this; + } + + /** + * @param fileMapping File mappings. File inputs only. + * @return this builder + */ + public DetectConfigurationBuilder fileMapping(List fileMapping) { + this.fileMapping = fileMapping; + return this; + } + + /** + * @param media Media options. File inputs only. + * @return this builder + */ + public DetectConfigurationBuilder media(Media media) { + this.media = media; + return this; + } + + public DetectConfiguration build() { + return new DetectConfiguration(this); + } + } +} diff --git a/common/src/main/java/com/skyflow/detect/DetectRunStatus.java b/common/src/main/java/com/skyflow/detect/DetectRunStatus.java new file mode 100644 index 00000000..3bb05886 --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/DetectRunStatus.java @@ -0,0 +1,12 @@ +package com.skyflow.detect; + +/** + * Lifecycle status of an asynchronous Detect file run. + */ +public enum DetectRunStatus { + UNKNOWN, + QUEUED, + IN_PROGRESS, + SUCCESS, + FAILED; +} diff --git a/common/src/main/java/com/skyflow/detect/DetectedEntity.java b/common/src/main/java/com/skyflow/detect/DetectedEntity.java new file mode 100644 index 00000000..6a443d1c --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/DetectedEntity.java @@ -0,0 +1,68 @@ +package com.skyflow.detect; + +import java.util.Map; + +/** + * An entity detected in the input text. + */ +public final class DetectedEntity { + private final String token; + private final String value; + private final EntityLocation location; + private final String entityType; + private final Map entityScores; + + public DetectedEntity(String token, String value, EntityLocation location, String entityType, Map entityScores) { + this.token = token; + this.value = value; + this.location = location; + this.entityType = entityType; + this.entityScores = entityScores; + } + + /** + * @return Replacement token, for example NAME_1. + */ + public String getToken() { + return token; + } + + /** + * @return Original value. Empty unless returnEntities is ALL. + */ + public String getValue() { + return value; + } + + /** + * @return Where the entity was found. + */ + public EntityLocation getLocation() { + return location; + } + + /** + * @return Entity type. Carries custom-type labels as well as built-in types. + */ + public String getEntityType() { + return entityType; + } + + /** + * @return Confidence scores by entity type, 0.0 to 1.0. + */ + public Map getEntityScores() { + return entityScores; + } + + @Override + public String toString() { + return "DetectedEntity{" + + "token=" + token + + ", value=" + value + + ", location=" + location + + ", entityType=" + entityType + + ", entityScores=" + entityScores + + '}'; + } +} diff --git a/common/src/main/java/com/skyflow/detect/Document.java b/common/src/main/java/com/skyflow/detect/Document.java new file mode 100644 index 00000000..2d461196 --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/Document.java @@ -0,0 +1,53 @@ +package com.skyflow.detect; + +/** + * Document processing options. + */ +public final class Document { + private final Pdf pdf; + + private Document(DocumentBuilder builder) { + this.pdf = builder.pdf; + } + + /** + * @return a new builder + */ + public static DocumentBuilder builder() { + return new DocumentBuilder(); + } + + /** + * @return PDF options. + */ + public Pdf getPdf() { + return pdf; + } + + @Override + public String toString() { + return "Document{" + + "pdf=" + pdf + + '}'; + } + + public static final class DocumentBuilder { + private Pdf pdf; + + private DocumentBuilder() { + } + + /** + * @param pdf PDF options. + * @return this builder + */ + public DocumentBuilder pdf(Pdf pdf) { + this.pdf = pdf; + return this; + } + + public Document build() { + return new Document(this); + } + } +} diff --git a/common/src/main/java/com/skyflow/detect/Entity.java b/common/src/main/java/com/skyflow/detect/Entity.java new file mode 100644 index 00000000..992acafd --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/Entity.java @@ -0,0 +1,113 @@ +package com.skyflow.detect; + +/** + * Configures how a single entity type is detected and de-identified. + */ +public final class Entity { + private final EntityType entityType; + private final DeidentificationType deidentificationType; + private final String destination; + private final Transformation transformation; + + private Entity(EntityBuilder builder) { + this.entityType = builder.entityType; + this.deidentificationType = builder.deidentificationType; + this.destination = builder.destination; + this.transformation = builder.transformation; + } + + /** + * @return a new builder + */ + public static EntityBuilder builder() { + return new EntityBuilder(); + } + + /** + * @return Entity type this rule applies to. Defaults to ALL on the server. + */ + public EntityType getEntityType() { + return entityType; + } + + /** + * @return Replacement strategy. Defaults to ENTITY_UNIQUE_COUNTER on the server. + */ + public DeidentificationType getDeidentificationType() { + return deidentificationType; + } + + /** + * @return Where vault tokens are stored: a table.column (skyvault) or token-group name (flowvault). Only used with VAULT_TOKEN. + */ + public String getDestination() { + return destination; + } + + /** + * @return Optional transformation applied to the detected value. + */ + public Transformation getTransformation() { + return transformation; + } + + @Override + public String toString() { + return "Entity{" + + "entityType=" + entityType + + ", deidentificationType=" + deidentificationType + + ", destination=" + destination + + ", transformation=" + transformation + + '}'; + } + + public static final class EntityBuilder { + private EntityType entityType; + private DeidentificationType deidentificationType; + private String destination; + private Transformation transformation; + + private EntityBuilder() { + } + + /** + * @param entityType Entity type this rule applies to. Defaults to ALL on the server. + * @return this builder + */ + public EntityBuilder entityType(EntityType entityType) { + this.entityType = entityType; + return this; + } + + /** + * @param deidentificationType Replacement strategy. Defaults to ENTITY_UNIQUE_COUNTER on the server. + * @return this builder + */ + public EntityBuilder deidentificationType(DeidentificationType deidentificationType) { + this.deidentificationType = deidentificationType; + return this; + } + + /** + * @param destination Where vault tokens are stored: a table.column (skyvault) or token-group name (flowvault). Only used with VAULT_TOKEN. + * @return this builder + */ + public EntityBuilder destination(String destination) { + this.destination = destination; + return this; + } + + /** + * @param transformation Optional transformation applied to the detected value. + * @return this builder + */ + public EntityBuilder transformation(Transformation transformation) { + this.transformation = transformation; + return this; + } + + public Entity build() { + return new Entity(this); + } + } +} diff --git a/common/src/main/java/com/skyflow/detect/EntityLocation.java b/common/src/main/java/com/skyflow/detect/EntityLocation.java new file mode 100644 index 00000000..88b45048 --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/EntityLocation.java @@ -0,0 +1,56 @@ +package com.skyflow.detect; + +/** + * Position of a detected entity in the original and processed text. + */ +public final class EntityLocation { + private final Integer startIndex; + private final Integer endIndex; + private final Integer startIndexProcessed; + private final Integer endIndexProcessed; + + public EntityLocation(Integer startIndex, Integer endIndex, Integer startIndexProcessed, Integer endIndexProcessed) { + this.startIndex = startIndex; + this.endIndex = endIndex; + this.startIndexProcessed = startIndexProcessed; + this.endIndexProcessed = endIndexProcessed; + } + + /** + * @return Start index in the original text. + */ + public Integer getStartIndex() { + return startIndex; + } + + /** + * @return End index in the original text. + */ + public Integer getEndIndex() { + return endIndex; + } + + /** + * @return Start index in the processed text. + */ + public Integer getStartIndexProcessed() { + return startIndexProcessed; + } + + /** + * @return End index in the processed text. + */ + public Integer getEndIndexProcessed() { + return endIndexProcessed; + } + + @Override + public String toString() { + return "EntityLocation{" + + "startIndex=" + startIndex + + ", endIndex=" + endIndex + + ", startIndexProcessed=" + startIndexProcessed + + ", endIndexProcessed=" + endIndexProcessed + + '}'; + } +} diff --git a/common/src/main/java/com/skyflow/detect/EntityType.java b/common/src/main/java/com/skyflow/detect/EntityType.java new file mode 100644 index 00000000..99cbbd8a --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/EntityType.java @@ -0,0 +1,78 @@ +package com.skyflow.detect; + +/** + * Entity types Skyflow Detect can identify. Also used to target entities in {@link RedactionLevel}. + */ +public enum EntityType { + NONE, + ACCOUNT_NUMBER, + AGE, + ALL, + BANK_ACCOUNT, + BLOOD_TYPE, + CONDITION, + CORPORATE_ACTION, + CREDIT_CARD, + CREDIT_CARD_EXPIRATION, + CVV, + DATE, + DATE_INTERVAL, + DAY, + DOB, + DOSE, + DRIVER_LICENSE, + DRUG, + DURATION, + EFFECT, + EMAIL_ADDRESS, + EVENT, + FILENAME, + FINANCIAL_METRIC, + GENDER, + HEALTHCARE_NUMBER, + INJURY, + IP_ADDRESS, + LANGUAGE, + LOCATION, + LOCATION_ADDRESS, + LOCATION_ADDRESS_STREET, + LOCATION_CITY, + LOCATION_COORDINATE, + LOCATION_COUNTRY, + LOCATION_STATE, + LOCATION_ZIP, + MARITAL_STATUS, + MEDICAL_CODE, + MEDICAL_PROCESS, + MONEY, + MONTH, + NAME, + NAME_FAMILY, + NAME_GIVEN, + NAME_MEDICAL_PROFESSIONAL, + NUMERICAL_PII, + OCCUPATION, + ORGANIZATION, + ORGANIZATION_ID, + ORGANIZATION_MEDICAL_FACILITY, + ORIGIN, + PASSPORT_NUMBER, + PASSWORD, + PHONE_NUMBER, + PHYSICAL_ATTRIBUTE, + POLITICAL_AFFILIATION, + PRODUCT, + PROJECT, + RELIGION, + ROUTING_NUMBER, + SEXUALITY, + SSN, + STATISTICS, + TIME, + TREND, + URL, + USERNAME, + VEHICLE_ID, + YEAR, + ZODIAC_SIGN; +} diff --git a/common/src/main/java/com/skyflow/detect/FileDataFormat.java b/common/src/main/java/com/skyflow/detect/FileDataFormat.java new file mode 100644 index 00000000..047cb54b --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/FileDataFormat.java @@ -0,0 +1,30 @@ +package com.skyflow.detect; + +/** + * File formats understood by Detect file operations. The wire value is the lower-case extension. + */ +public enum FileDataFormat { + BMP, + CSV, + DCM, + DOC, + DOCX, + GIF, + JPEG, + JPG, + JSON, + JSONL, + MP3, + PDF, + PNG, + PPT, + PPTX, + TIF, + TIFF, + TXT, + WAV, + XLS, + XLSX, + XML, + ZIP; +} diff --git a/common/src/main/java/com/skyflow/detect/FileMapping.java b/common/src/main/java/com/skyflow/detect/FileMapping.java new file mode 100644 index 00000000..da0663b0 --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/FileMapping.java @@ -0,0 +1,133 @@ +package com.skyflow.detect; + +/** + * Maps an input file location to output locations. File inputs only. + */ +public final class FileMapping { + private final String source; + private final String destination; + private final String outputFileNameSuffix; + private final String entitiesDestination; + private final String objectEntitiesDestination; + + private FileMapping(FileMappingBuilder builder) { + this.source = builder.source; + this.destination = builder.destination; + this.outputFileNameSuffix = builder.outputFileNameSuffix; + this.entitiesDestination = builder.entitiesDestination; + this.objectEntitiesDestination = builder.objectEntitiesDestination; + } + + /** + * @return a new builder + */ + public static FileMappingBuilder builder() { + return new FileMappingBuilder(); + } + + /** + * @return Source location. + */ + public String getSource() { + return source; + } + + /** + * @return Destination for the processed file. + */ + public String getDestination() { + return destination; + } + + /** + * @return Suffix appended to the processed file name. + */ + public String getOutputFileNameSuffix() { + return outputFileNameSuffix; + } + + /** + * @return Destination for the detected entities file. + */ + public String getEntitiesDestination() { + return entitiesDestination; + } + + /** + * @return Destination for the detected object entities file. + */ + public String getObjectEntitiesDestination() { + return objectEntitiesDestination; + } + + @Override + public String toString() { + return "FileMapping{" + + "source=" + source + + ", destination=" + destination + + ", outputFileNameSuffix=" + outputFileNameSuffix + + ", entitiesDestination=" + entitiesDestination + + ", objectEntitiesDestination=" + objectEntitiesDestination + + '}'; + } + + public static final class FileMappingBuilder { + private String source; + private String destination; + private String outputFileNameSuffix; + private String entitiesDestination; + private String objectEntitiesDestination; + + private FileMappingBuilder() { + } + + /** + * @param source Source location. + * @return this builder + */ + public FileMappingBuilder source(String source) { + this.source = source; + return this; + } + + /** + * @param destination Destination for the processed file. + * @return this builder + */ + public FileMappingBuilder destination(String destination) { + this.destination = destination; + return this; + } + + /** + * @param outputFileNameSuffix Suffix appended to the processed file name. + * @return this builder + */ + public FileMappingBuilder outputFileNameSuffix(String outputFileNameSuffix) { + this.outputFileNameSuffix = outputFileNameSuffix; + return this; + } + + /** + * @param entitiesDestination Destination for the detected entities file. + * @return this builder + */ + public FileMappingBuilder entitiesDestination(String entitiesDestination) { + this.entitiesDestination = entitiesDestination; + return this; + } + + /** + * @param objectEntitiesDestination Destination for the detected object entities file. + * @return this builder + */ + public FileMappingBuilder objectEntitiesDestination(String objectEntitiesDestination) { + this.objectEntitiesDestination = objectEntitiesDestination; + return this; + } + + public FileMapping build() { + return new FileMapping(this); + } + } +} diff --git a/common/src/main/java/com/skyflow/detect/FileOutput.java b/common/src/main/java/com/skyflow/detect/FileOutput.java new file mode 100644 index 00000000..a33cafca --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/FileOutput.java @@ -0,0 +1,46 @@ +package com.skyflow.detect; + +/** + * One output artifact of a Detect file run. + */ +public final class FileOutput { + private final String processedFile; + private final String processedFileType; + private final FileDataFormat processedFileExtension; + + public FileOutput(String processedFile, String processedFileType, FileDataFormat processedFileExtension) { + this.processedFile = processedFile; + this.processedFileType = processedFileType; + this.processedFileExtension = processedFileExtension; + } + + /** + * @return Base64 content or presigned URL, according to the run's outputType. + */ + public String getProcessedFile() { + return processedFile; + } + + /** + * @return Kind of output, for example REDACTED_FILE or ENTITIES. Reported as a string because the API does not enumerate it. + */ + public String getProcessedFileType() { + return processedFileType; + } + + /** + * @return Format of the output, or null when not reported. + */ + public FileDataFormat getProcessedFileExtension() { + return processedFileExtension; + } + + @Override + public String toString() { + return "FileOutput{" + + "processedFile=" + processedFile + + ", processedFileType=" + processedFileType + + ", processedFileExtension=" + processedFileExtension + + '}'; + } +} diff --git a/common/src/main/java/com/skyflow/detect/GetRunRequest.java b/common/src/main/java/com/skyflow/detect/GetRunRequest.java new file mode 100644 index 00000000..21a02689 --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/GetRunRequest.java @@ -0,0 +1,53 @@ +package com.skyflow.detect; + +/** + * Request for {@code getRun}: retrieves the status and outputs of a Detect file run. + */ +public final class GetRunRequest { + private final String runId; + + private GetRunRequest(GetRunRequestBuilder builder) { + this.runId = builder.runId; + } + + /** + * @return a new builder + */ + public static GetRunRequestBuilder builder() { + return new GetRunRequestBuilder(); + } + + /** + * @return Run id returned by {@code deidentifyFile}. Required. + */ + public String getRunId() { + return runId; + } + + @Override + public String toString() { + return "GetRunRequest{" + + "runId=" + runId + + '}'; + } + + public static final class GetRunRequestBuilder { + private String runId; + + private GetRunRequestBuilder() { + } + + /** + * @param runId Run id returned by {@code deidentifyFile}. Required. + * @return this builder + */ + public GetRunRequestBuilder runId(String runId) { + this.runId = runId; + return this; + } + + public GetRunRequest build() { + return new GetRunRequest(this); + } + } +} diff --git a/common/src/main/java/com/skyflow/detect/GetRunResponse.java b/common/src/main/java/com/skyflow/detect/GetRunResponse.java new file mode 100644 index 00000000..e8ffbc27 --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/GetRunResponse.java @@ -0,0 +1,78 @@ +package com.skyflow.detect; + +import java.util.List; + +/** + * Response of {@code getRun}. Output, outputType and metrics are populated only once the run has succeeded. + */ +public final class GetRunResponse { + private final String runId; + private final DetectRunStatus status; + private final DataSourceType outputType; + private final List output; + private final Metrics metrics; + private final String message; + + public GetRunResponse(String runId, DetectRunStatus status, DataSourceType outputType, List output, Metrics metrics, String message) { + this.runId = runId; + this.status = status; + this.outputType = outputType; + this.output = output; + this.metrics = metrics; + this.message = message; + } + + /** + * @return Run id that was queried. + */ + public String getRunId() { + return runId; + } + + /** + * @return Run status. UNKNOWN when the API did not report one. + */ + public DetectRunStatus getStatus() { + return status; + } + + /** + * @return How output files are delivered. Null while the run is pending. + */ + public DataSourceType getOutputType() { + return outputType; + } + + /** + * @return Output artifacts. Empty while the run is pending, never null. + */ + public List getOutput() { + return output; + } + + /** + * @return Processing metrics. Null while the run is pending. + */ + public Metrics getMetrics() { + return metrics; + } + + /** + * @return Failure detail when status is FAILED. + */ + public String getMessage() { + return message; + } + + @Override + public String toString() { + return "GetRunResponse{" + + "runId=" + runId + + ", status=" + status + + ", outputType=" + outputType + + ", output=" + output + + ", metrics=" + metrics + + ", message=" + message + + '}'; + } +} diff --git a/common/src/main/java/com/skyflow/detect/GuardrailsValidation.java b/common/src/main/java/com/skyflow/detect/GuardrailsValidation.java new file mode 100644 index 00000000..1d6f0f8c --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/GuardrailsValidation.java @@ -0,0 +1,11 @@ +package com.skyflow.detect; + +/** + * Overall result of a guardrails check. + */ +public enum GuardrailsValidation { + PASSED, + FAILED, + /** The API reported a value this SDK version does not know. */ + UNKNOWN; +} diff --git a/common/src/main/java/com/skyflow/detect/Image.java b/common/src/main/java/com/skyflow/detect/Image.java new file mode 100644 index 00000000..f1e33135 --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/Image.java @@ -0,0 +1,93 @@ +package com.skyflow.detect; + +/** + * Image processing options. + */ +public final class Image { + private final Boolean outputProcessedImage; + private final Boolean outputOcrText; + private final MaskingMethod maskingMethod; + + private Image(ImageBuilder builder) { + this.outputProcessedImage = builder.outputProcessedImage; + this.outputOcrText = builder.outputOcrText; + this.maskingMethod = builder.maskingMethod; + } + + /** + * @return a new builder + */ + public static ImageBuilder builder() { + return new ImageBuilder(); + } + + /** + * @return Whether to return the processed image. + */ + public Boolean getOutputProcessedImage() { + return outputProcessedImage; + } + + /** + * @return Whether to return OCR text. + */ + public Boolean getOutputOcrText() { + return outputOcrText; + } + + /** + * @return Masking method for detected regions. + */ + public MaskingMethod getMaskingMethod() { + return maskingMethod; + } + + @Override + public String toString() { + return "Image{" + + "outputProcessedImage=" + outputProcessedImage + + ", outputOcrText=" + outputOcrText + + ", maskingMethod=" + maskingMethod + + '}'; + } + + public static final class ImageBuilder { + private Boolean outputProcessedImage; + private Boolean outputOcrText; + private MaskingMethod maskingMethod; + + private ImageBuilder() { + } + + /** + * @param outputProcessedImage Whether to return the processed image. + * @return this builder + */ + public ImageBuilder outputProcessedImage(Boolean outputProcessedImage) { + this.outputProcessedImage = outputProcessedImage; + return this; + } + + /** + * @param outputOcrText Whether to return OCR text. + * @return this builder + */ + public ImageBuilder outputOcrText(Boolean outputOcrText) { + this.outputOcrText = outputOcrText; + return this; + } + + /** + * @param maskingMethod Masking method for detected regions. + * @return this builder + */ + public ImageBuilder maskingMethod(MaskingMethod maskingMethod) { + this.maskingMethod = maskingMethod; + return this; + } + + public Image build() { + return new Image(this); + } + } +} diff --git a/common/src/main/java/com/skyflow/detect/MaskingMethod.java b/common/src/main/java/com/skyflow/detect/MaskingMethod.java new file mode 100644 index 00000000..38402c52 --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/MaskingMethod.java @@ -0,0 +1,10 @@ +package com.skyflow.detect; + +/** + * Masking method applied to detected regions in images. + */ +public enum MaskingMethod { + NONE, + BLUR, + BLACKBOX; +} diff --git a/common/src/main/java/com/skyflow/detect/Media.java b/common/src/main/java/com/skyflow/detect/Media.java new file mode 100644 index 00000000..af6f24ac --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/Media.java @@ -0,0 +1,93 @@ +package com.skyflow.detect; + +/** + * Media-type specific processing options. File inputs only. + */ +public final class Media { + private final Audio audio; + private final Document document; + private final Image image; + + private Media(MediaBuilder builder) { + this.audio = builder.audio; + this.document = builder.document; + this.image = builder.image; + } + + /** + * @return a new builder + */ + public static MediaBuilder builder() { + return new MediaBuilder(); + } + + /** + * @return Audio options. + */ + public Audio getAudio() { + return audio; + } + + /** + * @return Document options. + */ + public Document getDocument() { + return document; + } + + /** + * @return Image options. + */ + public Image getImage() { + return image; + } + + @Override + public String toString() { + return "Media{" + + "audio=" + audio + + ", document=" + document + + ", image=" + image + + '}'; + } + + public static final class MediaBuilder { + private Audio audio; + private Document document; + private Image image; + + private MediaBuilder() { + } + + /** + * @param audio Audio options. + * @return this builder + */ + public MediaBuilder audio(Audio audio) { + this.audio = audio; + return this; + } + + /** + * @param document Document options. + * @return this builder + */ + public MediaBuilder document(Document document) { + this.document = document; + return this; + } + + /** + * @param image Image options. + * @return this builder + */ + public MediaBuilder image(Image image) { + this.image = image; + return this; + } + + public Media build() { + return new Media(this); + } + } +} diff --git a/common/src/main/java/com/skyflow/detect/Metrics.java b/common/src/main/java/com/skyflow/detect/Metrics.java new file mode 100644 index 00000000..46becb12 --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/Metrics.java @@ -0,0 +1,76 @@ +package com.skyflow.detect; + +/** + * Processing metrics returned by Detect operations. + */ +public final class Metrics { + private final Double size; + private final Integer wordCount; + private final Integer characterCount; + private final Integer slides; + private final Integer pages; + private final Double duration; + + public Metrics(Double size, Integer wordCount, Integer characterCount, Integer slides, Integer pages, Double duration) { + this.size = size; + this.wordCount = wordCount; + this.characterCount = characterCount; + this.slides = slides; + this.pages = pages; + this.duration = duration; + } + + /** + * @return Input size in kilobytes. + */ + public Double getSize() { + return size; + } + + /** + * @return Number of words. + */ + public Integer getWordCount() { + return wordCount; + } + + /** + * @return Number of characters. + */ + public Integer getCharacterCount() { + return characterCount; + } + + /** + * @return Number of slides. + */ + public Integer getSlides() { + return slides; + } + + /** + * @return Number of pages. + */ + public Integer getPages() { + return pages; + } + + /** + * @return Media duration in seconds. + */ + public Double getDuration() { + return duration; + } + + @Override + public String toString() { + return "Metrics{" + + "size=" + size + + ", wordCount=" + wordCount + + ", characterCount=" + characterCount + + ", slides=" + slides + + ", pages=" + pages + + ", duration=" + duration + + '}'; + } +} diff --git a/common/src/main/java/com/skyflow/detect/ObjectDeidentificationType.java b/common/src/main/java/com/skyflow/detect/ObjectDeidentificationType.java new file mode 100644 index 00000000..13f3fd6e --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/ObjectDeidentificationType.java @@ -0,0 +1,10 @@ +package com.skyflow.detect; + +/** + * How a detected visual object is handled. + */ +public enum ObjectDeidentificationType { + UNKNOWN, + REDACT, + UNREDACT; +} diff --git a/common/src/main/java/com/skyflow/detect/ObjectEntity.java b/common/src/main/java/com/skyflow/detect/ObjectEntity.java new file mode 100644 index 00000000..1f62853b --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/ObjectEntity.java @@ -0,0 +1,73 @@ +package com.skyflow.detect; + +/** + * Configures detection of a visual object type in images or video. + */ +public final class ObjectEntity { + private final ObjectEntityType entityType; + private final ObjectDeidentificationType deidentificationType; + + private ObjectEntity(ObjectEntityBuilder builder) { + this.entityType = builder.entityType; + this.deidentificationType = builder.deidentificationType; + } + + /** + * @return a new builder + */ + public static ObjectEntityBuilder builder() { + return new ObjectEntityBuilder(); + } + + /** + * @return Object type this rule applies to. + */ + public ObjectEntityType getEntityType() { + return entityType; + } + + /** + * @return How matches are handled. + */ + public ObjectDeidentificationType getDeidentificationType() { + return deidentificationType; + } + + @Override + public String toString() { + return "ObjectEntity{" + + "entityType=" + entityType + + ", deidentificationType=" + deidentificationType + + '}'; + } + + public static final class ObjectEntityBuilder { + private ObjectEntityType entityType; + private ObjectDeidentificationType deidentificationType; + + private ObjectEntityBuilder() { + } + + /** + * @param entityType Object type this rule applies to. + * @return this builder + */ + public ObjectEntityBuilder entityType(ObjectEntityType entityType) { + this.entityType = entityType; + return this; + } + + /** + * @param deidentificationType How matches are handled. + * @return this builder + */ + public ObjectEntityBuilder deidentificationType(ObjectDeidentificationType deidentificationType) { + this.deidentificationType = deidentificationType; + return this; + } + + public ObjectEntity build() { + return new ObjectEntity(this); + } + } +} diff --git a/common/src/main/java/com/skyflow/detect/ObjectEntityType.java b/common/src/main/java/com/skyflow/detect/ObjectEntityType.java new file mode 100644 index 00000000..679917c7 --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/ObjectEntityType.java @@ -0,0 +1,13 @@ +package com.skyflow.detect; + +/** + * Visual objects Skyflow Detect can identify in images and video. + */ +public enum ObjectEntityType { + NONE, + FACE, + LICENSE_PLATE, + LOGO, + SIGNATURE, + ALL; +} diff --git a/common/src/main/java/com/skyflow/detect/OutputTranscriptionType.java b/common/src/main/java/com/skyflow/detect/OutputTranscriptionType.java new file mode 100644 index 00000000..ed2638ed --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/OutputTranscriptionType.java @@ -0,0 +1,12 @@ +package com.skyflow.detect; + +/** + * Transcription output produced for audio inputs. + */ +public enum OutputTranscriptionType { + NONE, + TRANSCRIPTION, + MEDICAL_TRANSCRIPTION, + DIARIZED_TRANSCRIPTION, + MEDICAL_DIARIZED_TRANSCRIPTION; +} diff --git a/common/src/main/java/com/skyflow/detect/Pdf.java b/common/src/main/java/com/skyflow/detect/Pdf.java new file mode 100644 index 00000000..d147d632 --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/Pdf.java @@ -0,0 +1,93 @@ +package com.skyflow.detect; + +/** + * PDF processing options. + */ +public final class Pdf { + private final PdfProcessingMode processingMode; + private final Integer density; + private final Integer maxResolution; + + private Pdf(PdfBuilder builder) { + this.processingMode = builder.processingMode; + this.density = builder.density; + this.maxResolution = builder.maxResolution; + } + + /** + * @return a new builder + */ + public static PdfBuilder builder() { + return new PdfBuilder(); + } + + /** + * @return Processing mode. + */ + public PdfProcessingMode getProcessingMode() { + return processingMode; + } + + /** + * @return Rendering density (DPI). + */ + public Integer getDensity() { + return density; + } + + /** + * @return Maximum rendering resolution. + */ + public Integer getMaxResolution() { + return maxResolution; + } + + @Override + public String toString() { + return "Pdf{" + + "processingMode=" + processingMode + + ", density=" + density + + ", maxResolution=" + maxResolution + + '}'; + } + + public static final class PdfBuilder { + private PdfProcessingMode processingMode; + private Integer density; + private Integer maxResolution; + + private PdfBuilder() { + } + + /** + * @param processingMode Processing mode. + * @return this builder + */ + public PdfBuilder processingMode(PdfProcessingMode processingMode) { + this.processingMode = processingMode; + return this; + } + + /** + * @param density Rendering density (DPI). + * @return this builder + */ + public PdfBuilder density(Integer density) { + this.density = density; + return this; + } + + /** + * @param maxResolution Maximum rendering resolution. + * @return this builder + */ + public PdfBuilder maxResolution(Integer maxResolution) { + this.maxResolution = maxResolution; + return this; + } + + public Pdf build() { + return new Pdf(this); + } + } +} diff --git a/common/src/main/java/com/skyflow/detect/PdfProcessingMode.java b/common/src/main/java/com/skyflow/detect/PdfProcessingMode.java new file mode 100644 index 00000000..f32399ba --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/PdfProcessingMode.java @@ -0,0 +1,10 @@ +package com.skyflow.detect; + +/** + * How PDF documents are processed. + */ +public enum PdfProcessingMode { + NONE, + OCR, + TEXT_LAYER; +} diff --git a/common/src/main/java/com/skyflow/detect/PollOptions.java b/common/src/main/java/com/skyflow/detect/PollOptions.java new file mode 100644 index 00000000..c1aa656a --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/PollOptions.java @@ -0,0 +1,91 @@ +package com.skyflow.detect; + +/** + * Opts {@code deidentifyFile} into submit-and-poll mode. When present, the SDK polls the run with + * exponential backoff (1, 2, 4, 8, 16, then 32 seconds) until it reaches a terminal status, the + * wait time is used up, or the attempt limit is hit. An empty {@code PollOptions.builder().build()} + * uses the defaults. + */ +public final class PollOptions { + public static final int DEFAULT_WAIT_TIME_SECONDS = 60; + public static final int MAX_WAIT_TIME_SECONDS = 300; + public static final int DEFAULT_MAX_ATTEMPTS = 23; + public static final int MAX_MAX_ATTEMPTS = 23; + + private final Integer waitTime; + private final Integer maxAttempts; + + private PollOptions(PollOptionsBuilder builder) { + this.waitTime = builder.waitTime; + this.maxAttempts = builder.maxAttempts; + } + + /** + * @return a new builder + */ + public static PollOptionsBuilder builder() { + return new PollOptionsBuilder(); + } + + /** + * @return Maximum total seconds to keep polling, or null for the default of 60. At most 300. + */ + public Integer getWaitTime() { + return waitTime; + } + + /** + * @return Maximum number of run lookups, or null for the default of 23. At most 23. + */ + public Integer getMaxAttempts() { + return maxAttempts; + } + + /** @return the wait time with the default applied */ + public int effectiveWaitTime() { + return waitTime == null ? DEFAULT_WAIT_TIME_SECONDS : waitTime; + } + + /** @return the attempt limit with the default applied */ + public int effectiveMaxAttempts() { + return maxAttempts == null ? DEFAULT_MAX_ATTEMPTS : maxAttempts; + } + + @Override + public String toString() { + return "PollOptions{" + + "waitTime=" + waitTime + + ", maxAttempts=" + maxAttempts + + '}'; + } + + public static final class PollOptionsBuilder { + private Integer waitTime; + private Integer maxAttempts; + + private PollOptionsBuilder() { + } + + /** + * @param waitTime Maximum total seconds to keep polling. 1 to 300; default 60. + * @return this builder + */ + public PollOptionsBuilder waitTime(Integer waitTime) { + this.waitTime = waitTime; + return this; + } + + /** + * @param maxAttempts Maximum number of run lookups. 1 to 23; default 23. + * @return this builder + */ + public PollOptionsBuilder maxAttempts(Integer maxAttempts) { + this.maxAttempts = maxAttempts; + return this; + } + + public PollOptions build() { + return new PollOptions(this); + } + } +} diff --git a/common/src/main/java/com/skyflow/detect/RedactionLevel.java b/common/src/main/java/com/skyflow/detect/RedactionLevel.java new file mode 100644 index 00000000..74f04f1e --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/RedactionLevel.java @@ -0,0 +1,113 @@ +package com.skyflow.detect; + +/** + * Controls how one group of tokens is rendered when re-identifying. Supply exactly one source (entityName or tokenGroupName) and exactly one replacement (redactionType or redactionPattern). + */ +public final class RedactionLevel { + private final EntityType entityName; + private final RedactionType redactionType; + private final String tokenGroupName; + private final String redactionPattern; + + private RedactionLevel(RedactionLevelBuilder builder) { + this.entityName = builder.entityName; + this.redactionType = builder.redactionType; + this.tokenGroupName = builder.tokenGroupName; + this.redactionPattern = builder.redactionPattern; + } + + /** + * @return a new builder + */ + public static RedactionLevelBuilder builder() { + return new RedactionLevelBuilder(); + } + + /** + * @return Entity to target. skyvault vaults. + */ + public EntityType getEntityName() { + return entityName; + } + + /** + * @return Replacement rendering. skyvault vaults. + */ + public RedactionType getRedactionType() { + return redactionType; + } + + /** + * @return Token group to target. flowvault vaults. + */ + public String getTokenGroupName() { + return tokenGroupName; + } + + /** + * @return Named redaction pattern to apply. flowvault vaults. + */ + public String getRedactionPattern() { + return redactionPattern; + } + + @Override + public String toString() { + return "RedactionLevel{" + + "entityName=" + entityName + + ", redactionType=" + redactionType + + ", tokenGroupName=" + tokenGroupName + + ", redactionPattern=" + redactionPattern + + '}'; + } + + public static final class RedactionLevelBuilder { + private EntityType entityName; + private RedactionType redactionType; + private String tokenGroupName; + private String redactionPattern; + + private RedactionLevelBuilder() { + } + + /** + * @param entityName Entity to target. skyvault vaults. + * @return this builder + */ + public RedactionLevelBuilder entityName(EntityType entityName) { + this.entityName = entityName; + return this; + } + + /** + * @param redactionType Replacement rendering. skyvault vaults. + * @return this builder + */ + public RedactionLevelBuilder redactionType(RedactionType redactionType) { + this.redactionType = redactionType; + return this; + } + + /** + * @param tokenGroupName Token group to target. flowvault vaults. + * @return this builder + */ + public RedactionLevelBuilder tokenGroupName(String tokenGroupName) { + this.tokenGroupName = tokenGroupName; + return this; + } + + /** + * @param redactionPattern Named redaction pattern to apply. flowvault vaults. + * @return this builder + */ + public RedactionLevelBuilder redactionPattern(String redactionPattern) { + this.redactionPattern = redactionPattern; + return this; + } + + public RedactionLevel build() { + return new RedactionLevel(this); + } + } +} diff --git a/common/src/main/java/com/skyflow/detect/RedactionType.java b/common/src/main/java/com/skyflow/detect/RedactionType.java new file mode 100644 index 00000000..87b3e891 --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/RedactionType.java @@ -0,0 +1,11 @@ +package com.skyflow.detect; + +/** + * How a token is rendered when re-identifying (skyvault vaults). + */ +public enum RedactionType { + PLAIN_TEXT, + MASKED, + DEFAULT, + REDACTED; +} diff --git a/common/src/main/java/com/skyflow/detect/ReidentifyStringRequest.java b/common/src/main/java/com/skyflow/detect/ReidentifyStringRequest.java new file mode 100644 index 00000000..23626297 --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/ReidentifyStringRequest.java @@ -0,0 +1,75 @@ +package com.skyflow.detect; + +import java.util.List; + +/** + * Request for {@code reidentifyString}. + */ +public final class ReidentifyStringRequest { + private final String text; + private final List redactionLevel; + + private ReidentifyStringRequest(ReidentifyStringRequestBuilder builder) { + this.text = builder.text; + this.redactionLevel = builder.redactionLevel; + } + + /** + * @return a new builder + */ + public static ReidentifyStringRequestBuilder builder() { + return new ReidentifyStringRequestBuilder(); + } + + /** + * @return Text containing tokens to re-identify. Required. + */ + public String getText() { + return text; + } + + /** + * @return Optional per-group rendering rules. + */ + public List getRedactionLevel() { + return redactionLevel; + } + + @Override + public String toString() { + return "ReidentifyStringRequest{" + + "text=" + text + + ", redactionLevel=" + redactionLevel + + '}'; + } + + public static final class ReidentifyStringRequestBuilder { + private String text; + private List redactionLevel; + + private ReidentifyStringRequestBuilder() { + } + + /** + * @param text Text containing tokens to re-identify. Required. + * @return this builder + */ + public ReidentifyStringRequestBuilder text(String text) { + this.text = text; + return this; + } + + /** + * @param redactionLevel Optional per-group rendering rules. + * @return this builder + */ + public ReidentifyStringRequestBuilder redactionLevel(List redactionLevel) { + this.redactionLevel = redactionLevel; + return this; + } + + public ReidentifyStringRequest build() { + return new ReidentifyStringRequest(this); + } + } +} diff --git a/common/src/main/java/com/skyflow/detect/ReidentifyStringResponse.java b/common/src/main/java/com/skyflow/detect/ReidentifyStringResponse.java new file mode 100644 index 00000000..e1825bf2 --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/ReidentifyStringResponse.java @@ -0,0 +1,36 @@ +package com.skyflow.detect; + +/** + * Response of {@code reidentifyString}. + */ +public final class ReidentifyStringResponse { + private final String processedText; + private final Metrics metrics; + + public ReidentifyStringResponse(String processedText, Metrics metrics) { + this.processedText = processedText; + this.metrics = metrics; + } + + /** + * @return Re-identified text. + */ + public String getProcessedText() { + return processedText; + } + + /** + * @return Processing metrics. + */ + public Metrics getMetrics() { + return metrics; + } + + @Override + public String toString() { + return "ReidentifyStringResponse{" + + "processedText=" + processedText + + ", metrics=" + metrics + + '}'; + } +} diff --git a/common/src/main/java/com/skyflow/detect/ReturnEntitiesType.java b/common/src/main/java/com/skyflow/detect/ReturnEntitiesType.java new file mode 100644 index 00000000..3ba4fa79 --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/ReturnEntitiesType.java @@ -0,0 +1,10 @@ +package com.skyflow.detect; + +/** + * Controls which detected entities are returned in the response. + */ +public enum ReturnEntitiesType { + EXCLUDE_SENSITIVE_DATA, + ALL, + NONE; +} diff --git a/common/src/main/java/com/skyflow/detect/ShiftDates.java b/common/src/main/java/com/skyflow/detect/ShiftDates.java new file mode 100644 index 00000000..fd4adc24 --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/ShiftDates.java @@ -0,0 +1,73 @@ +package com.skyflow.detect; + +/** + * Shifts detected dates by a random number of days within the given range. + */ +public final class ShiftDates { + private final Integer minDays; + private final Integer maxDays; + + private ShiftDates(ShiftDatesBuilder builder) { + this.minDays = builder.minDays; + this.maxDays = builder.maxDays; + } + + /** + * @return a new builder + */ + public static ShiftDatesBuilder builder() { + return new ShiftDatesBuilder(); + } + + /** + * @return Minimum number of days to shift. + */ + public Integer getMinDays() { + return minDays; + } + + /** + * @return Maximum number of days to shift. + */ + public Integer getMaxDays() { + return maxDays; + } + + @Override + public String toString() { + return "ShiftDates{" + + "minDays=" + minDays + + ", maxDays=" + maxDays + + '}'; + } + + public static final class ShiftDatesBuilder { + private Integer minDays; + private Integer maxDays; + + private ShiftDatesBuilder() { + } + + /** + * @param minDays Minimum number of days to shift. + * @return this builder + */ + public ShiftDatesBuilder minDays(Integer minDays) { + this.minDays = minDays; + return this; + } + + /** + * @param maxDays Maximum number of days to shift. + * @return this builder + */ + public ShiftDatesBuilder maxDays(Integer maxDays) { + this.maxDays = maxDays; + return this; + } + + public ShiftDates build() { + return new ShiftDates(this); + } + } +} diff --git a/common/src/main/java/com/skyflow/detect/Transformation.java b/common/src/main/java/com/skyflow/detect/Transformation.java new file mode 100644 index 00000000..5fb9cb69 --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/Transformation.java @@ -0,0 +1,53 @@ +package com.skyflow.detect; + +/** + * Transformations applied to detected entities before they are written to the output. + */ +public final class Transformation { + private final ShiftDates shiftDates; + + private Transformation(TransformationBuilder builder) { + this.shiftDates = builder.shiftDates; + } + + /** + * @return a new builder + */ + public static TransformationBuilder builder() { + return new TransformationBuilder(); + } + + /** + * @return Date shifting configuration. + */ + public ShiftDates getShiftDates() { + return shiftDates; + } + + @Override + public String toString() { + return "Transformation{" + + "shiftDates=" + shiftDates + + '}'; + } + + public static final class TransformationBuilder { + private ShiftDates shiftDates; + + private TransformationBuilder() { + } + + /** + * @param shiftDates Date shifting configuration. + * @return this builder + */ + public TransformationBuilder shiftDates(ShiftDates shiftDates) { + this.shiftDates = shiftDates; + return this; + } + + public Transformation build() { + return new Transformation(this); + } + } +} diff --git a/common/src/main/java/com/skyflow/detect/internal/DeidentifyFileFlow.java b/common/src/main/java/com/skyflow/detect/internal/DeidentifyFileFlow.java new file mode 100644 index 00000000..4d825983 --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/internal/DeidentifyFileFlow.java @@ -0,0 +1,41 @@ +package com.skyflow.detect.internal; + +import com.skyflow.detect.DeidentifyFileRequest; +import com.skyflow.detect.DeidentifyFileResponse; +import com.skyflow.detect.DetectRunStatus; +import com.skyflow.detect.GetRunResponse; +import com.skyflow.detect.polling.RunPoller; +import com.skyflow.errors.SkyflowException; +import com.skyflow.logs.InfoLogs; +import com.skyflow.utils.BaseUtils; +import com.skyflow.utils.logger.LogUtil; + +/** + * Everything {@code deidentifyFile} does after the submit call, shared by every SDK: decide whether to + * poll, poll, merge the run into the response, and write outputs for file-based requests. Each + * controller performs the submit itself and hands the run id here. + */ +public final class DeidentifyFileFlow { + private DeidentifyFileFlow() { + } + + public static DeidentifyFileResponse complete(DeidentifyFileRequest request, String runId, RunPoller.RunFetcher fetcher) + throws SkyflowException { + return complete(request, runId, fetcher, Thread::sleep); + } + + static DeidentifyFileResponse complete(DeidentifyFileRequest request, String runId, RunPoller.RunFetcher fetcher, + RunPoller.Sleeper sleeper) throws SkyflowException { + if (request.getPollOptions() == null || runId == null) { + LogUtil.printInfoLog(BaseUtils.parameterizedString(InfoLogs.DEIDENTIFY_FILE_RUN_SUBMITTED.getLog(), String.valueOf(runId))); + return DetectResponseMapper.submittedDeidentifyFileResponse(runId); + } + GetRunResponse run = RunPoller.poll(runId, request.getPollOptions(), fetcher, sleeper); + DeidentifyFileResponse response = DetectResponseMapper.toDeidentifyFileResponse(runId, run); + if (response.getStatus() == DetectRunStatus.SUCCESS && request.getFile() != null) { + FileOutputWriter.write(response.getOutput(), response.getOutputType(), request.getFile().getName(), + request.getOutputDirectory()); + } + return response; + } +} diff --git a/common/src/main/java/com/skyflow/detect/internal/DetectRequestMapper.java b/common/src/main/java/com/skyflow/detect/internal/DetectRequestMapper.java new file mode 100644 index 00000000..42ef0faf --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/internal/DetectRequestMapper.java @@ -0,0 +1,407 @@ +package com.skyflow.detect.internal; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Base64; +import java.util.List; + +import com.skyflow.detect.Audio; +import com.skyflow.detect.Bleep; +import com.skyflow.detect.CheckGuardrailsRequest; +import com.skyflow.detect.CustomType; +import com.skyflow.detect.DataSourceType; +import com.skyflow.detect.DeidentifyFileRequest; +import com.skyflow.detect.DeidentifyStringRequest; +import com.skyflow.detect.FileDataFormat; +import com.skyflow.detect.Detect; +import com.skyflow.detect.DetectConfiguration; +import com.skyflow.detect.Document; +import com.skyflow.detect.Entity; +import com.skyflow.detect.FileMapping; +import com.skyflow.detect.Image; +import com.skyflow.detect.Media; +import com.skyflow.detect.ObjectEntity; +import com.skyflow.detect.Pdf; +import com.skyflow.detect.RedactionLevel; +import com.skyflow.detect.ReidentifyStringRequest; +import com.skyflow.detect.ShiftDates; +import com.skyflow.detect.Transformation; +import com.skyflow.errors.BaseErrorMessage; +import com.skyflow.errors.ErrorCode; +import com.skyflow.errors.SkyflowException; +import com.skyflow.generated.detect.rest.resources.filesv2.requests.DeidentifyFileRequestV2; +import com.skyflow.generated.detect.rest.resources.filesv2.types.DeidentifyFileRequestV2DataFormat; +import com.skyflow.generated.detect.rest.resources.filesv2.types.DeidentifyFileRequestV2DataSource; +import com.skyflow.generated.detect.rest.resources.guardrailsv2.requests.DetectGuardrailsRequestV2; +import com.skyflow.logs.ErrorLogs; +import com.skyflow.utils.BaseUtils; +import com.skyflow.utils.logger.LogUtil; +import com.skyflow.generated.detect.rest.resources.stringsv2.requests.DeidentifyStringRequestV2; +import com.skyflow.generated.detect.rest.resources.stringsv2.requests.ReidentifyStringRequestV2; +import com.skyflow.generated.detect.rest.types.AudioV2; +import com.skyflow.generated.detect.rest.types.AudioV2Bleep; +import com.skyflow.generated.detect.rest.types.AudioV2OutputTranscription; +import com.skyflow.generated.detect.rest.types.DetectConfigV2; +import com.skyflow.generated.detect.rest.types.DetectCustomTypes; +import com.skyflow.generated.detect.rest.types.DetectEntity; +import com.skyflow.generated.detect.rest.types.DetectEntityDeidentificationType; +import com.skyflow.generated.detect.rest.types.DetectEntityEntityType; +import com.skyflow.generated.detect.rest.types.DetectObjectEntity; +import com.skyflow.generated.detect.rest.types.DetectObjectEntityDeidentificationType; +import com.skyflow.generated.detect.rest.types.DetectObjectEntityEntityType; +import com.skyflow.generated.detect.rest.types.DetectReturnEntities; +import com.skyflow.generated.detect.rest.types.DocumentV2; +import com.skyflow.generated.detect.rest.types.DocumentV2Pdf; +import com.skyflow.generated.detect.rest.types.DocumentV2PdfProcessingMode; +import com.skyflow.generated.detect.rest.types.EntityTransformation; +import com.skyflow.generated.detect.rest.types.ImageV2; +import com.skyflow.generated.detect.rest.types.ImageV2MaskingMethod; +import com.skyflow.generated.detect.rest.types.RedactionLevelEntityName; +import com.skyflow.generated.detect.rest.types.RedactionLevelRedactionType; +import com.skyflow.generated.detect.rest.types.TransformationShiftDates; + +/** + * Converts public SDK request models into generated V2 API requests. Internal to the SDK. + */ +public final class DetectRequestMapper { + private DetectRequestMapper() { + } + + public static DeidentifyStringRequestV2 toDeidentifyStringRequest(DeidentifyStringRequest request, String vaultId) { + DeidentifyStringRequestV2._FinalStage builder = DeidentifyStringRequestV2.builder().text(request.getText()); + if (request.getConfigurationId() != null) { + builder.configurationId(request.getConfigurationId()); + } + if (request.getConfiguration() != null) { + builder.configuration(toDetectConfig(request.getConfiguration(), vaultId)); + } + return builder.build(); + } + + /** + * Builds the submit request. For file-based requests this reads and base64-encodes the file and infers + * the format from its extension when none was given. + */ + public static DeidentifyFileRequestV2 toDeidentifyFileRequest(DeidentifyFileRequest request, String vaultId) + throws SkyflowException { + String value = request.getValue(); + FileDataFormat format = request.getDataFormat(); + File file = request.getFile(); + if (file != null) { + value = encodeFile(file); + if (format == null) { + format = inferFormat(file.getName()); + } + } + // A local file is always sent as BASE64, whatever dataSource the builder was left with. + DataSourceType dataSource = file != null ? DataSourceType.BASE64 : request.getDataSource(); + DeidentifyFileRequestV2._FinalStage builder = DeidentifyFileRequestV2.builder() + .dataSource(DeidentifyFileRequestV2DataSource.valueOf(dataSource.name())) + .value(value); + if (format != null) { + // The wire value for a file format is the lower-case extension. + builder.dataFormat(DeidentifyFileRequestV2DataFormat.valueOf(format.name().toLowerCase())); + } + if (request.getConfigurationId() != null) { + builder.configurationId(request.getConfigurationId()); + } + if (request.getConfiguration() != null) { + builder.configuration(toDetectConfig(request.getConfiguration(), vaultId)); + } + return builder.build(); + } + + static String encodeFile(File file) throws SkyflowException { + try { + return Base64.getEncoder().encodeToString(Files.readAllBytes(file.toPath())); + } catch (IOException | OutOfMemoryError e) { + LogUtil.printErrorLog(BaseUtils.parameterizedString(ErrorLogs.FAILED_TO_ENCODE_FILE_IN_DEIDENTIFY_FILE.getLog(), "detect")); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.FailedToEncodeFile.getMessage()); + } + } + + /** @return the format matching the file extension, or null when unknown so the server infers it */ + static FileDataFormat inferFormat(String fileName) { + int dot = fileName == null ? -1 : fileName.lastIndexOf('.'); + if (dot < 0 || dot == fileName.length() - 1) { + return null; + } + try { + return FileDataFormat.valueOf(fileName.substring(dot + 1).toUpperCase()); + } catch (IllegalArgumentException unknown) { + return null; + } + } + + public static ReidentifyStringRequestV2 toReidentifyStringRequest(ReidentifyStringRequest request, String vaultId) { + ReidentifyStringRequestV2._FinalStage builder = ReidentifyStringRequestV2.builder() + .vaultId(vaultId) + .text(request.getText()); + if (request.getRedactionLevel() != null) { + builder.redactionLevel(toRedactionLevels(request.getRedactionLevel())); + } + return builder.build(); + } + + public static DetectGuardrailsRequestV2 toCheckGuardrailsRequest(CheckGuardrailsRequest request, String vaultId) { + DetectGuardrailsRequestV2._FinalStage builder = DetectGuardrailsRequestV2.builder() + .text(request.getText()) + .vaultId(vaultId); + if (request.getCheckToxicity() != null) { + builder.checkToxicity(request.getCheckToxicity()); + } + if (request.getDenyTopics() != null) { + builder.denyTopics(request.getDenyTopics()); + } + return builder.build(); + } + + public static DetectConfigV2 toDetectConfig(DetectConfiguration configuration, String vaultId) { + DetectConfigV2._FinalStage builder = DetectConfigV2.builder().vaultId(vaultId); + if (configuration.getName() != null) { + builder.name(configuration.getName()); + } + if (configuration.getDescription() != null) { + builder.description(configuration.getDescription()); + } + if (configuration.getDetect() != null) { + builder.detect(toDetect(configuration.getDetect())); + } + if (configuration.getFileMapping() != null) { + builder.fileMapping(toFileMappings(configuration.getFileMapping())); + } + if (configuration.getMedia() != null) { + builder.media(toMedia(configuration.getMedia())); + } + return builder.build(); + } + + static com.skyflow.generated.detect.rest.types.Detect toDetect(Detect detect) { + com.skyflow.generated.detect.rest.types.Detect.Builder builder = + com.skyflow.generated.detect.rest.types.Detect.builder(); + if (detect.getEntities() != null) { + List entities = new ArrayList<>(); + for (Entity entity : detect.getEntities()) { + if (entity != null) { + entities.add(toEntity(entity)); + } + } + builder.entities(entities); + } + if (detect.getObjectEntities() != null) { + List objectEntities = new ArrayList<>(); + for (ObjectEntity objectEntity : detect.getObjectEntities()) { + if (objectEntity != null) { + objectEntities.add(toObjectEntity(objectEntity)); + } + } + builder.objectEntities(objectEntities); + } + if (detect.getRestrict() != null) { + builder.restrict(detect.getRestrict()); + } + if (detect.getSkip() != null) { + builder.skip(detect.getSkip()); + } + if (detect.getCustomTypes() != null) { + List customTypes = new ArrayList<>(); + for (CustomType customType : detect.getCustomTypes()) { + if (customType != null) { + customTypes.add(toCustomType(customType)); + } + } + builder.customTypes(customTypes); + } + if (detect.getReturnEntities() != null) { + builder.returnEntities(DetectReturnEntities.valueOf(detect.getReturnEntities().name())); + } + return builder.build(); + } + + static DetectEntity toEntity(Entity entity) { + DetectEntity.Builder builder = DetectEntity.builder(); + if (entity.getEntityType() != null) { + builder.entityType(DetectEntityEntityType.valueOf(entity.getEntityType().name())); + } + if (entity.getDeidentificationType() != null) { + builder.deidentificationType(DetectEntityDeidentificationType.valueOf(entity.getDeidentificationType().name())); + } + if (entity.getDestination() != null) { + builder.destination(entity.getDestination()); + } + if (entity.getTransformation() != null) { + builder.transformation(toTransformation(entity.getTransformation())); + } + return builder.build(); + } + + static EntityTransformation toTransformation(Transformation transformation) { + EntityTransformation.Builder builder = EntityTransformation.builder(); + ShiftDates shiftDates = transformation.getShiftDates(); + if (shiftDates != null) { + TransformationShiftDates.Builder shift = TransformationShiftDates.builder(); + if (shiftDates.getMinDays() != null) { + shift.minDays(shiftDates.getMinDays()); + } + if (shiftDates.getMaxDays() != null) { + shift.maxDays(shiftDates.getMaxDays()); + } + builder.shiftDates(shift.build()); + } + return builder.build(); + } + + static DetectCustomTypes toCustomType(CustomType customType) { + DetectCustomTypes.Builder builder = DetectCustomTypes.builder(); + if (customType.getLabel() != null) { + builder.label(customType.getLabel()); + } + if (customType.getMatch() != null) { + builder.match(customType.getMatch()); + } + return builder.build(); + } + + static DetectObjectEntity toObjectEntity(ObjectEntity objectEntity) { + DetectObjectEntity.Builder builder = DetectObjectEntity.builder(); + if (objectEntity.getEntityType() != null) { + builder.entityType(DetectObjectEntityEntityType.valueOf(objectEntity.getEntityType().name())); + } + if (objectEntity.getDeidentificationType() != null) { + builder.deidentificationType( + DetectObjectEntityDeidentificationType.valueOf(objectEntity.getDeidentificationType().name())); + } + return builder.build(); + } + + static List toFileMappings(List fileMappings) { + List result = new ArrayList<>(); + for (FileMapping mapping : fileMappings) { + if (mapping == null) { + continue; + } + com.skyflow.generated.detect.rest.types.FileMapping.Builder builder = + com.skyflow.generated.detect.rest.types.FileMapping.builder(); + if (mapping.getSource() != null) { + builder.source(mapping.getSource()); + } + if (mapping.getDestination() != null) { + builder.destination(mapping.getDestination()); + } + if (mapping.getOutputFileNameSuffix() != null) { + builder.outputFileNameSuffix(mapping.getOutputFileNameSuffix()); + } + if (mapping.getEntitiesDestination() != null) { + builder.entitiesDestination(mapping.getEntitiesDestination()); + } + if (mapping.getObjectEntitiesDestination() != null) { + builder.objectEntitiesDestination(mapping.getObjectEntitiesDestination()); + } + result.add(builder.build()); + } + return result; + } + + static com.skyflow.generated.detect.rest.types.Media toMedia(Media media) { + com.skyflow.generated.detect.rest.types.Media.Builder builder = + com.skyflow.generated.detect.rest.types.Media.builder(); + if (media.getAudio() != null) { + builder.audio(toAudio(media.getAudio())); + } + if (media.getDocument() != null) { + builder.document(toDocument(media.getDocument())); + } + if (media.getImage() != null) { + builder.image(toImage(media.getImage())); + } + return builder.build(); + } + + static AudioV2 toAudio(Audio audio) { + AudioV2.Builder builder = AudioV2.builder(); + if (audio.getOutputProcessedAudio() != null) { + builder.outputProcessedAudio(audio.getOutputProcessedAudio()); + } + if (audio.getOutputTranscription() != null) { + builder.outputTranscription(AudioV2OutputTranscription.valueOf(audio.getOutputTranscription().name())); + } + Bleep bleep = audio.getBleep(); + if (bleep != null) { + AudioV2Bleep.Builder bleepBuilder = AudioV2Bleep.builder(); + if (bleep.getStartPadding() != null) { + bleepBuilder.startPadding(bleep.getStartPadding()); + } + if (bleep.getStopPadding() != null) { + bleepBuilder.stopPadding(bleep.getStopPadding()); + } + if (bleep.getFrequency() != null) { + bleepBuilder.frequency(bleep.getFrequency()); + } + if (bleep.getGain() != null) { + bleepBuilder.gain(bleep.getGain()); + } + builder.bleep(bleepBuilder.build()); + } + return builder.build(); + } + + static DocumentV2 toDocument(Document document) { + DocumentV2.Builder builder = DocumentV2.builder(); + Pdf pdf = document.getPdf(); + if (pdf != null) { + DocumentV2Pdf.Builder pdfBuilder = DocumentV2Pdf.builder(); + if (pdf.getProcessingMode() != null) { + pdfBuilder.processingMode(DocumentV2PdfProcessingMode.valueOf(pdf.getProcessingMode().name())); + } + if (pdf.getDensity() != null) { + pdfBuilder.density(pdf.getDensity()); + } + if (pdf.getMaxResolution() != null) { + pdfBuilder.maxResolution(pdf.getMaxResolution()); + } + builder.pdf(pdfBuilder.build()); + } + return builder.build(); + } + + static ImageV2 toImage(Image image) { + ImageV2.Builder builder = ImageV2.builder(); + if (image.getOutputProcessedImage() != null) { + builder.outputProcessedImage(image.getOutputProcessedImage()); + } + if (image.getOutputOcrText() != null) { + builder.outputOcrText(image.getOutputOcrText()); + } + if (image.getMaskingMethod() != null) { + builder.maskingMethod(ImageV2MaskingMethod.valueOf(image.getMaskingMethod().name())); + } + return builder.build(); + } + + public static List toRedactionLevels(List levels) { + List result = new ArrayList<>(); + for (RedactionLevel level : levels) { + if (level == null) { + continue; + } + com.skyflow.generated.detect.rest.types.RedactionLevel.Builder builder = + com.skyflow.generated.detect.rest.types.RedactionLevel.builder(); + if (level.getEntityName() != null) { + builder.entityName(RedactionLevelEntityName.valueOf(level.getEntityName().name())); + } + if (level.getRedactionType() != null) { + builder.redactionType(RedactionLevelRedactionType.valueOf(level.getRedactionType().name())); + } + if (level.getTokenGroupName() != null) { + builder.tokenGroupName(level.getTokenGroupName()); + } + if (level.getRedactionPattern() != null) { + builder.redactionPattern(level.getRedactionPattern()); + } + result.add(builder.build()); + } + return result; + } +} diff --git a/common/src/main/java/com/skyflow/detect/internal/DetectResponseMapper.java b/common/src/main/java/com/skyflow/detect/internal/DetectResponseMapper.java new file mode 100644 index 00000000..f4d2484b --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/internal/DetectResponseMapper.java @@ -0,0 +1,145 @@ +package com.skyflow.detect.internal; + +import com.skyflow.detect.CheckGuardrailsResponse; +import com.skyflow.detect.DataSourceType; +import com.skyflow.detect.DeidentifyFileResponse; +import com.skyflow.detect.DeidentifyStringResponse; +import com.skyflow.detect.DetectRunStatus; +import com.skyflow.detect.FileDataFormat; +import com.skyflow.detect.FileOutput; +import com.skyflow.detect.GetRunResponse; +import com.skyflow.detect.GuardrailsValidation; +import com.skyflow.detect.DetectedEntity; +import com.skyflow.detect.EntityLocation; +import com.skyflow.detect.Metrics; +import com.skyflow.detect.ReidentifyStringResponse; +import com.skyflow.generated.detect.rest.types.DeidentifyStringResponseV2; +import com.skyflow.generated.detect.rest.types.DeidentifyStringResponseV2DetectedEntity; +import com.skyflow.generated.detect.rest.types.DeidentifyStringResponseV2EntityLocation; +import com.skyflow.generated.detect.rest.types.DetectGuardrailsResponseV2; +import com.skyflow.generated.detect.rest.types.DetectRunsResponseV2; +import com.skyflow.generated.detect.rest.types.ReidentifyStringResponseV2; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * Converts generated V2 API responses into public SDK response models. Internal to the SDK. + */ +public final class DetectResponseMapper { + private DetectResponseMapper() { + } + + public static DeidentifyStringResponse toDeidentifyStringResponse(DeidentifyStringResponseV2 response) { + List entities = new ArrayList<>(); + List apiEntities = response.getEntities().orElse(null); + if (apiEntities != null) { + for (DeidentifyStringResponseV2DetectedEntity apiEntity : apiEntities) { + if (apiEntity != null) { + entities.add(toDetectedEntity(apiEntity)); + } + } + } + return new DeidentifyStringResponse( + response.getProcessedText().orElse(null), + Collections.unmodifiableList(entities), + response.getMetrics().map(DetectResponseMapper::toMetrics).orElse(null)); + } + + public static ReidentifyStringResponse toReidentifyStringResponse(ReidentifyStringResponseV2 response) { + return new ReidentifyStringResponse( + response.getProcessedText().orElse(null), + response.getMetrics().map(DetectResponseMapper::toMetrics).orElse(null)); + } + + static DetectedEntity toDetectedEntity(DeidentifyStringResponseV2DetectedEntity apiEntity) { + Map scores = apiEntity.getEntityScores().orElse(null); + return new DetectedEntity( + apiEntity.getToken().orElse(null), + apiEntity.getValue().orElse(null), + apiEntity.getLocation().map(DetectResponseMapper::toEntityLocation).orElse(null), + apiEntity.getEntityType().orElse(null), + scores == null ? Collections.emptyMap() : Collections.unmodifiableMap(scores)); + } + + static EntityLocation toEntityLocation(DeidentifyStringResponseV2EntityLocation location) { + return new EntityLocation( + location.getStartIndex().orElse(null), + location.getEndIndex().orElse(null), + location.getStartIndexProcessed().orElse(null), + location.getEndIndexProcessed().orElse(null)); + } + + public static CheckGuardrailsResponse toCheckGuardrailsResponse(DetectGuardrailsResponseV2 response) { + return new CheckGuardrailsResponse( + response.getText(), + enumOrDefault(GuardrailsValidation.class, + response.getValidation() == null ? null : response.getValidation().toString(), GuardrailsValidation.UNKNOWN), + response.getToxic().orElse(null), + response.getDeniedTopic().orElse(null)); + } + + public static GetRunResponse toGetRunResponse(String runId, DetectRunsResponseV2 response) { + List outputs = new ArrayList<>(); + List apiOutputs = response.getOutput().orElse(null); + if (apiOutputs != null) { + for (com.skyflow.generated.detect.rest.types.FileOutput apiOutput : apiOutputs) { + if (apiOutput != null) { + outputs.add(toFileOutput(apiOutput)); + } + } + } + return new GetRunResponse( + runId, + response.getStatus().map(s -> enumOrDefault(DetectRunStatus.class, s.toString(), DetectRunStatus.UNKNOWN)) + .orElse(DetectRunStatus.UNKNOWN), + response.getOutputType().map(t -> enumOrDefault(DataSourceType.class, t.toString(), null)).orElse(null), + Collections.unmodifiableList(outputs), + response.getMetrics().map(DetectResponseMapper::toMetrics).orElse(null), + response.getMessage().orElse(null)); + } + + /** Response for a submit-only call: run id and nothing else. */ + public static DeidentifyFileResponse submittedDeidentifyFileResponse(String runId) { + return new DeidentifyFileResponse(runId, null, null, Collections.emptyList(), null, null); + } + + /** Merges the submit's run id with the polled run. */ + public static DeidentifyFileResponse toDeidentifyFileResponse(String runId, GetRunResponse run) { + return new DeidentifyFileResponse(runId, run.getStatus(), run.getOutputType(), run.getOutput(), + run.getMetrics(), run.getMessage()); + } + + public static FileOutput toFileOutput(com.skyflow.generated.detect.rest.types.FileOutput apiOutput) { + return new FileOutput( + apiOutput.getProcessedFile().orElse(null), + apiOutput.getProcessedFileType().map(Object::toString).orElse(null), + apiOutput.getProcessedFileExtension() + .map(e -> enumOrDefault(FileDataFormat.class, e.toString().toUpperCase(), null)) + .orElse(null)); + } + + /** Maps a wire value onto a public enum, tolerating values this SDK version does not know. */ + static > E enumOrDefault(Class type, String value, E fallback) { + if (value == null) { + return fallback; + } + try { + return Enum.valueOf(type, value); + } catch (IllegalArgumentException unknown) { + return fallback; + } + } + + public static Metrics toMetrics(com.skyflow.generated.detect.rest.types.Metrics metrics) { + return new Metrics( + metrics.getSize().orElse(null), + metrics.getWordCount().orElse(null), + metrics.getCharacterCount().orElse(null), + metrics.getSlides().orElse(null), + metrics.getPages().orElse(null), + metrics.getDuration().orElse(null)); + } +} diff --git a/common/src/main/java/com/skyflow/detect/internal/DetectValidations.java b/common/src/main/java/com/skyflow/detect/internal/DetectValidations.java new file mode 100644 index 00000000..6836dcff --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/internal/DetectValidations.java @@ -0,0 +1,114 @@ +package com.skyflow.detect.internal; + +import com.skyflow.detect.CheckGuardrailsRequest; +import com.skyflow.detect.DeidentifyFileRequest; +import com.skyflow.detect.DeidentifyStringRequest; +import com.skyflow.detect.PollOptions; +import com.skyflow.detect.GetRunRequest; +import com.skyflow.detect.ReidentifyStringRequest; +import com.skyflow.errors.BaseErrorMessage; +import com.skyflow.errors.ErrorCode; +import com.skyflow.errors.SkyflowException; +import com.skyflow.logs.ErrorLogs; +import com.skyflow.utils.BaseUtils; +import com.skyflow.utils.logger.LogUtil; + + +/** + * Client-side validation for Detect V2 requests. Internal to the SDK. + */ +public final class DetectValidations { + static final String INTERFACE_NAME = "detect"; + + private DetectValidations() { + } + + public static void validateDeidentifyStringRequest(DeidentifyStringRequest request) throws SkyflowException { + if (request == null) { + throw invalid(ErrorLogs.DEIDENTIFY_STRING_REQUEST_NULL, BaseErrorMessage.DeidentifyStringRequestNull); + } + if (isBlank(request.getText())) { + throw invalid(ErrorLogs.INVALID_TEXT_IN_DEIDENTIFY_STRING, BaseErrorMessage.InvalidTextInDeidentifyString); + } + // configurationId and configuration are both optional on the API and are passed through as given. + } + + public static void validateDeidentifyFileRequest(DeidentifyFileRequest request) throws SkyflowException { + if (request == null) { + throw invalid(ErrorLogs.DEIDENTIFY_FILE_REQUEST_NULL, BaseErrorMessage.DeidentifyFileRequestNull); + } + if (request.getFile() != null) { + if (!isBlank(request.getValue())) { + throw invalid(ErrorLogs.MULTIPLE_FILE_SOURCES_IN_DEIDENTIFY_FILE, BaseErrorMessage.MultipleFileSourcesInDeidentifyFile); + } + if (!request.getFile().isFile()) { + throw invalid(ErrorLogs.FILE_NOT_FOUND_IN_DEIDENTIFY_FILE, BaseErrorMessage.FileNotFoundToDeidentify); + } + } else { + if (request.getDataSource() == null) { + throw invalid(ErrorLogs.INVALID_DATA_SOURCE_IN_DEIDENTIFY_FILE, BaseErrorMessage.InvalidDataSourceInDeidentifyFile); + } + if (isBlank(request.getValue())) { + throw invalid(ErrorLogs.INVALID_VALUE_IN_DEIDENTIFY_FILE, BaseErrorMessage.InvalidValueInDeidentifyFile); + } + } + // configurationId and configuration are both optional on the API and are passed through as given. + validatePollOptions(request.getPollOptions()); + if (request.getPollOptions() == null && !isBlank(request.getOutputDirectory())) { + throw invalid(ErrorLogs.OUTPUT_DIRECTORY_WITHOUT_POLL_OPTIONS, BaseErrorMessage.OutputDirectoryWithoutPollOptions); + } + } + + static void validatePollOptions(PollOptions options) throws SkyflowException { + if (options == null) { + return; + } + Integer waitTime = options.getWaitTime(); + if (waitTime != null && (waitTime < 1 || waitTime > PollOptions.MAX_WAIT_TIME_SECONDS)) { + throw invalid(ErrorLogs.INVALID_WAIT_TIME_IN_POLL_OPTIONS, BaseErrorMessage.InvalidWaitTimeInPollOptions); + } + Integer maxAttempts = options.getMaxAttempts(); + if (maxAttempts != null && (maxAttempts < 1 || maxAttempts > PollOptions.MAX_MAX_ATTEMPTS)) { + throw invalid(ErrorLogs.INVALID_MAX_ATTEMPTS_IN_POLL_OPTIONS, BaseErrorMessage.InvalidMaxAttemptsInPollOptions); + } + } + + public static void validateReidentifyStringRequest(ReidentifyStringRequest request) throws SkyflowException { + if (request == null) { + throw invalid(ErrorLogs.REIDENTIFY_STRING_REQUEST_NULL, BaseErrorMessage.ReidentifyStringRequestNull); + } + if (isBlank(request.getText())) { + throw invalid(ErrorLogs.INVALID_TEXT_IN_REIDENTIFY_STRING, BaseErrorMessage.InvalidTextInReidentifyString); + } + // redactionLevel entries are optional on the API and are passed through as given. + } + + public static void validateGetRunRequest(GetRunRequest request) throws SkyflowException { + if (request == null) { + throw invalid(ErrorLogs.GET_RUN_REQUEST_NULL, BaseErrorMessage.GetRunRequestNull); + } + if (isBlank(request.getRunId())) { + throw invalid(ErrorLogs.INVALID_RUN_ID_IN_GET_RUN, BaseErrorMessage.InvalidRunIdInGetRun); + } + } + + public static void validateCheckGuardrailsRequest(CheckGuardrailsRequest request) throws SkyflowException { + if (request == null) { + throw invalid(ErrorLogs.CHECK_GUARDRAILS_REQUEST_NULL, BaseErrorMessage.CheckGuardrailsRequestNull); + } + if (isBlank(request.getText())) { + throw invalid(ErrorLogs.INVALID_TEXT_IN_CHECK_GUARDRAILS, BaseErrorMessage.InvalidTextInCheckGuardrails); + } + // checkToxicity and denyTopics are optional on the API and are passed through as given. + } + + + static boolean isBlank(String value) { + return value == null || value.trim().isEmpty(); + } + + private static SkyflowException invalid(ErrorLogs log, BaseErrorMessage message) { + LogUtil.printErrorLog(BaseUtils.parameterizedString(log.getLog(), INTERFACE_NAME)); + return new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), message.getMessage()); + } +} diff --git a/common/src/main/java/com/skyflow/detect/internal/FileOutputWriter.java b/common/src/main/java/com/skyflow/detect/internal/FileOutputWriter.java new file mode 100644 index 00000000..f82f0957 --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/internal/FileOutputWriter.java @@ -0,0 +1,118 @@ +package com.skyflow.detect.internal; + +import com.skyflow.detect.DataSourceType; +import com.skyflow.detect.FileOutput; +import com.skyflow.errors.BaseErrorMessage; +import com.skyflow.errors.SkyflowException; +import com.skyflow.logs.ErrorLogs; +import com.skyflow.logs.InfoLogs; +import com.skyflow.utils.BaseConstants; +import com.skyflow.utils.BaseUtils; +import com.skyflow.utils.logger.LogUtil; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Base64; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Writes the base64 outputs of a successful file run next to, or into a chosen directory for, the + * local file that was submitted. Internal to the SDK. + * + *

Each output becomes {@code processed-.}. Entity outputs carry their + * type in the name so they never collide with a processed file of the same extension: ENTITIES + * becomes {@code processed-entities-.json} and OBJECT_ENTITIES becomes + * {@code processed-object-entities-.json}. If two outputs would still share a name + * the file type is added before the extension. Presigned-URL outputs are not fetched and nothing + * is written for them. + */ +public final class FileOutputWriter { + private static final String ENTITIES_TYPE = "ENTITIES"; + + private FileOutputWriter() { + } + + /** + * @return the files written, in output order; empty when the output type is not BASE64 + * @throws SkyflowException if the directory cannot be created or a file cannot be decoded or written + */ + public static List write(List outputs, DataSourceType outputType, String inputFileName, + String outputDirectory) throws SkyflowException { + List written = new ArrayList<>(); + if (outputs == null || outputs.isEmpty()) { + return written; + } + if (outputType != DataSourceType.BASE64) { + LogUtil.printInfoLog(BaseUtils.parameterizedString(InfoLogs.OUTPUT_NOT_WRITTEN_FOR_OUTPUT_TYPE.getLog(), + String.valueOf(outputType))); + return written; + } + File directory = outputDirectory == null || outputDirectory.trim().isEmpty() ? null : new File(outputDirectory); + if (directory != null && !directory.isDirectory() && !directory.mkdirs()) { + throw writeFailure(ErrorLogs.OUTPUT_DIRECTORY_NOT_CREATED, directory.getPath(), null); + } + String base = stripExtension(inputFileName); + Set used = new HashSet<>(); + for (FileOutput output : outputs) { + if (output == null || output.getProcessedFile() == null) { + continue; + } + String name = fileName(base, output, used); + File target = directory == null ? new File(name) : new File(directory, name); + try { + byte[] bytes = Base64.getDecoder().decode(output.getProcessedFile()); + Files.write(target.toPath(), bytes); + } catch (IllegalArgumentException | IOException e) { + throw writeFailure(ErrorLogs.OUTPUT_FILE_NOT_WRITTEN, target.getPath(), e); + } + LogUtil.printInfoLog(BaseUtils.parameterizedString(InfoLogs.OUTPUT_FILE_WRITTEN.getLog(), target.getPath())); + written.add(target); + } + return written; + } + + /** + * @param base the input file name without its extension + * @return {@code processed-.}, or {@code processed--.json} for entity outputs + */ + static String fileName(String base, FileOutput output, Set used) { + String type = output.getProcessedFileType() == null ? "" : output.getProcessedFileType(); + boolean entities = type.toUpperCase().contains(ENTITIES_TYPE); + String extension; + if (output.getProcessedFileExtension() != null) { + extension = output.getProcessedFileExtension().name().toLowerCase(); + } else if (entities) { + extension = "json"; + } else { + extension = "bin"; + } + // ENTITIES -> processed-entities-, OBJECT_ENTITIES -> processed-object-entities- + String stem = entities + ? BaseConstants.PROCESSED_FILE_NAME_PREFIX + type.toLowerCase().replace('_', '-') + "-" + base + : BaseConstants.PROCESSED_FILE_NAME_PREFIX + base; + String name = stem + "." + extension; + if (!used.add(name)) { + name = stem + "." + (type.isEmpty() ? String.valueOf(used.size()) : type.toLowerCase()) + "." + extension; + used.add(name); + } + return name; + } + + static String stripExtension(String fileName) { + if (fileName == null || fileName.isEmpty()) { + return "output"; + } + int dot = fileName.lastIndexOf('.'); + return dot > 0 ? fileName.substring(0, dot) : fileName; + } + + private static SkyflowException writeFailure(ErrorLogs log, String path, Exception cause) { + LogUtil.printErrorLog(BaseUtils.parameterizedString(log.getLog(), path)); + String message = BaseUtils.parameterizedString(BaseErrorMessage.FailedToWriteOutputFile.getMessage(), path); + return cause == null ? new SkyflowException(message) : new SkyflowException(message, cause); + } +} diff --git a/common/src/main/java/com/skyflow/detect/polling/RunPoller.java b/common/src/main/java/com/skyflow/detect/polling/RunPoller.java new file mode 100644 index 00000000..4aff5a31 --- /dev/null +++ b/common/src/main/java/com/skyflow/detect/polling/RunPoller.java @@ -0,0 +1,99 @@ +package com.skyflow.detect.polling; + +import com.skyflow.detect.DetectRunStatus; +import com.skyflow.detect.FileOutput; +import com.skyflow.detect.GetRunResponse; +import com.skyflow.detect.PollOptions; +import com.skyflow.errors.SkyflowException; +import com.skyflow.logs.InfoLogs; +import com.skyflow.utils.BaseUtils; +import com.skyflow.utils.logger.LogUtil; + +import java.util.Collections; + +/** + * Polls a Detect file run until it reaches a terminal status or the {@link PollOptions} budget is used + * up. Shared by every SDK: the caller supplies how a run is fetched, so each controller plugs in its + * own {@code getRun}. + * + *

Backoff is exponential, 1, 2, 4, 8, 16 then 32 seconds between lookups, with the last sleep + * trimmed so the total never exceeds {@code waitTime}. The first lookup happens immediately after the + * submit. SUCCESS, FAILED and UNKNOWN are terminal; every other status keeps polling. + */ +public final class RunPoller { + /** Longest single pause between two lookups, in seconds. */ + public static final long MAX_DELAY_SECONDS = 32L; + + /** Fetches the current state of a run. */ + public interface RunFetcher { + GetRunResponse fetch(String runId) throws SkyflowException; + } + + /** Pauses the calling thread. Replaceable in tests. */ + public interface Sleeper { + void sleep(long millis) throws InterruptedException; + } + + private RunPoller() { + } + + /** + * Polls on the calling thread. + * + * @param runId run to poll + * @param options wait time and attempt limit, defaults applied + * @param fetcher how to look the run up + * @return the terminal run, or the last non-terminal run seen when the budget ran out or the thread was interrupted + * @throws SkyflowException if a lookup fails + */ + public static GetRunResponse poll(String runId, PollOptions options, RunFetcher fetcher) throws SkyflowException { + return poll(runId, options, fetcher, Thread::sleep); + } + + public static GetRunResponse poll(String runId, PollOptions options, RunFetcher fetcher, Sleeper sleeper) + throws SkyflowException { + int waitTime = options.effectiveWaitTime(); + int maxAttempts = options.effectiveMaxAttempts(); + LogUtil.printInfoLog(BaseUtils.parameterizedString(InfoLogs.POLL_RUN_STARTED.getLog(), runId, + String.valueOf(waitTime), String.valueOf(maxAttempts))); + + long elapsedSeconds = 0; + long delaySeconds = 1; + int attempts = 0; + while (true) { + GetRunResponse run = fetcher.fetch(runId); + attempts++; + if (run == null || run.getStatus() == null) { + // A fetcher that reports nothing is treated as an unknown, terminal state. + run = new GetRunResponse(runId, DetectRunStatus.UNKNOWN, null, Collections.emptyList(), null, null); + } + switch (run.getStatus()) { + case SUCCESS: + case FAILED: + case UNKNOWN: + LogUtil.printInfoLog(BaseUtils.parameterizedString(InfoLogs.POLL_RUN_TERMINAL.getLog(), runId, + run.getStatus().name(), String.valueOf(attempts))); + return run; + default: + // QUEUED, IN_PROGRESS: keep polling while the budget allows. + break; + } + if (attempts >= maxAttempts || elapsedSeconds >= waitTime) { + LogUtil.printInfoLog(BaseUtils.parameterizedString(InfoLogs.POLL_RUN_BUDGET_EXHAUSTED.getLog(), runId, + run.getStatus().name(), String.valueOf(attempts), String.valueOf(elapsedSeconds))); + return run; + } + long sleepSeconds = Math.min(delaySeconds, waitTime - elapsedSeconds); + try { + sleeper.sleep(sleepSeconds * 1000L); + } catch (InterruptedException interrupted) { + Thread.currentThread().interrupt(); + LogUtil.printInfoLog(BaseUtils.parameterizedString(InfoLogs.POLL_RUN_INTERRUPTED.getLog(), runId, + run.getStatus().name())); + return run; + } + elapsedSeconds += sleepSeconds; + delaySeconds = Math.min(delaySeconds * 2, MAX_DELAY_SECONDS); + } + } +} diff --git a/common/src/main/java/com/skyflow/errors/ErrorMessage.java b/common/src/main/java/com/skyflow/errors/BaseErrorMessage.java similarity index 72% rename from common/src/main/java/com/skyflow/errors/ErrorMessage.java rename to common/src/main/java/com/skyflow/errors/BaseErrorMessage.java index 1e408cca..d3c9c388 100644 --- a/common/src/main/java/com/skyflow/errors/ErrorMessage.java +++ b/common/src/main/java/com/skyflow/errors/BaseErrorMessage.java @@ -2,7 +2,12 @@ import com.skyflow.utils.SdkVersion; -public enum ErrorMessage { +/** + * Validation and client-side error messages shared by every SDK. Named {@code BaseErrorMessage} + * rather than {@code ErrorMessage} so it can never be shadowed by a module's own + * {@code com.skyflow.errors.ErrorMessage}: code in common must reference this enum. + */ +public enum BaseErrorMessage { // Client initialization VaultIdAlreadyInConfigList("%s0 Validation error. VaultId is present in an existing config. Specify a new vaultId in config."), VaultIdNotInConfigList("%s0 Validation error. VaultId is missing from the config. Specify the vaultIds from configs."), @@ -12,18 +17,11 @@ public enum ErrorMessage { ConnectionIdNotInConfigList("%s0 Validation error. ConnectionId is missing from the config. Specify the connectionIds from configs."), EmptyCredentials("%s0 Validation error. Invalid credentials. Credentials must not be empty."), NullCredentials("%s0 Validation error. Credentials object is null. Specify a valid Credentials object."), - TableSpecifiedInRequestAndRecordObject("%s0 Validation error. Table name cannot be specified at both the request and record levels. Please specify the table name in only one place."), - UpsertTableRequestAtRecordLevel("%s0 Validation error. Table name should be present at each record level when upsert is present at record level."), - UpsertTableRequestAtRequestLevel("%s0 Validation error. Upsert should be present at each record level when table name is present at record level."), - TableNotSpecifiedInRequestAndRecordObject("%s0 Validation error. Table name is missing. Table name should be specified at one place either at the request level or record level. Please specify the table name at one place."), // Vault config InvalidVaultId("%s0 Initialization failed. Invalid vault ID. Specify a valid vault ID."), EmptyVaultId("%s0 Initialization failed. Invalid vault ID. Vault ID must not be empty."), InvalidClusterId("%s0 Initialization failed. Invalid cluster ID. Specify cluster ID."), EmptyClusterId("%s0 Initialization failed. Invalid cluster ID. Specify a valid cluster ID."), - EmptyVaultUrl("%s0 Initialization failed. Vault URL is empty. Specify a valid vault URL."), - InvalidVaultUrlFormat("%s0 Initialization failed. Vault URL must start with 'https://'."), - EitherVaultUrlOrClusterIdRequired("%s0 Initialization failed. Specify either 'clusterId' or 'vaultURL'."), // Connection config InvalidConnectionId("%s0 Initialization failed. Invalid connection ID. Specify a valid connection ID."), @@ -69,20 +67,11 @@ public enum ErrorMessage { TableKeyError("%s0 Validation error. 'table' key is missing from the payload. Specify a 'table' key."), EmptyTable("%s0 Validation error. 'table' can't be empty. Specify a table."), ValuesKeyError("%s0 Validation error. 'values' key is missing from the payload. Specify a 'values' key."), - EmptyRecords("%s0 Validation error. 'records' can't be empty. Specify records."), - EmptyKeyInRecords("%s0 Validation error. Invalid key in data in records. Specify a valid key."), - EmptyValueInRecords("%s0 Validation error. Invalid value in records. Specify a valid value."), - RecordsKeyError("%s0 Validation error. 'records' key is missing from the payload. Specify a 'records' key."), - EmptyValues("%s0 Validation error. 'values' can't be empty. Specify values."), EmptyKeyInValues("%s0 Validation error. Invalid key in values. Specify a valid key."), - EmptyValueInValues("%s0 Validation error. Invalid value in values. Specify a valid value."), TokensKeyError("%s0 Validation error. 'tokens' key is missing from the payload. Specify a 'tokens' key."), EmptyTokens("%s0 Validation error. The 'tokens' field is empty. Specify tokens for one or more fields."), EmptyKeyInTokens("%s0 Validation error. Invalid key tokens. Specify a valid key."), - EmptyValueInTokens("%s0 Validation error. Invalid value in tokens. Specify a valid value."), EmptyUpsert("%s0 Validation error. 'upsert' key can't be empty. Specify an upsert column."), - InvalidUpsertUpdateType("%s0 Validation error. Invalid upsert updateType. Specify either 'UPDATE' or 'REPLACE'."), - EmptyUpsertValues("%s0 Validation error. Upsert column values can't be empty. Specify at least one upsert column."), HomogenousNotSupportedWithUpsert("%s0 Validation error. 'homogenous' is not supported with 'upsert'. Specify either 'homogenous' or 'upsert'."), TokensPassedForTokenModeDisable("%s0 Validation error. 'tokenMode' wasn't specified. Set 'tokenMode' to 'ENABLE' to insert tokens."), NoTokensWithTokenMode("%s0 Validation error. Tokens weren't specified for records while 'tokenMode' was %s1. Specify tokens."), @@ -90,22 +79,15 @@ public enum ErrorMessage { InsufficientTokensPassedForTokenModeEnableStrict("%s0 Validation error. 'tokenMode' is set to 'ENABLE_STRICT', but some fields are missing tokens. Specify tokens for all fields."), BatchInsertPartialSuccess("%s0 Insert operation completed with partial success."), BatchInsertFailure("%s0 Insert operation failed."), - RecordSizeExceedError("%s0 Maximum number of records exceeded. The limit is 100000."), // Detokenize InvalidDetokenizeData("%s0 Validation error. Invalid detokenize data. Specify valid detokenize data."), EmptyDetokenizeData("%s0 Validation error. Invalid data tokens. Specify at least one data token."), EmptyTokenInDetokenizeData("%s0 Validation error. Invalid data tokens. Specify a valid data token."), - TokensSizeExceedError("%s0 Maximum number of tokens exceeded. The limit is 100000."), // Delete DeleteRequestNull("%s0 Validation error. DeleteRequest object is null. Specify a valid DeleteRequest object."), - // Delete Tokens - DeleteTokensRequestNull("%s0 Validation error. DeleteTokensRequest object is null. Specify a valid DeleteTokensRequest object."), - EmptyDeleteTokensData("%s0 Validation error. Tokens list is empty. Specify at least one token to delete."), - EmptyTokenInDeleteTokensData("%s0 Validation error. Invalid token in delete tokens request. Specify a valid token."), - DeleteTokensSizeExceedError("%s0 Maximum number of tokens exceeded. The limit is 100000."), // Get GetRequestNull("%s0 Validation error. GetRequest object is null. Specify a valid GetRequest object."), @@ -126,15 +108,6 @@ public enum ErrorMessage { ColumnValuesKeyErrorGet("%s0 Validation error. 'columnValues' aren't specified whereas 'columnName' is specified. Either add 'columnValues' or remove 'columnName'."), EmptyColumnValues("%s0 Validation error. 'columnValues' can't be empty. Specify at least one column value"), EmptyValueInColumnValues("%s0 Validation error. Invalid value in column values. Specify a valid column value."), - IdsOrUniqueValuesKeyError("%s0 Validation error. 'ids' or 'uniqueValues' key is missing from the payload. Specify ids or uniqueValues in payload."), - BothIdsAndUniqueValuesSpecified("%s0 Validation error. Both Skyflow IDs and unique values can't be specified. Either specify Skyflow IDs or unique values."), - EmptyUniqueValues("%s0 Validation error. 'uniqueValues' can't be empty. Specify at least one unique value."), - EmptyUniqueValueInUniqueValues("%s0 Validation error. Invalid unique value in 'uniqueValues'. Specify a valid unique value."), - NullColumnRedactions("%s0 Validation error. Column redaction object can not be null. Specify a valid column redaction object."), - NullColumnNameInColumnRedaction("%s0 Validation error. Column name can not be null or empty in column redaction. Specify a valid column name."), - NullRedactionInColumnRedaction("%s0 Validation error. Redaction can not be null or empty in column redaction. Specify a valid redaction."), - BothSingleTableFieldsAndRecordsSpecified("%s0 Validation error. Both single-table lookup fields ('table', 'ids', 'fields', 'uniqueValues', 'columnRedactions') and 'records' can't be specified. Either specify single-table fields or 'records'."), - NullGetRecordRequest("%s0 Validation error. Record in 'records' is null. Specify a valid record."), TokenKeyError("%s0 Validation error. 'token' key is missing from the payload. Specify a 'token' key."), PartialSuccess("%s0 Validation error. Check 'SkyflowError.data' for details."), @@ -146,9 +119,6 @@ public enum ErrorMessage { InvalidSkyflowIdType("%s0 Validation error. Invalid type for 'skyflow_id' in data payload. Specify 'skyflow_id' as a string."), EmptySkyflowId("%s0 Validation error. 'skyflow_id' can't be empty. Specify a skyflow id."), UpdateRequestNull("%s0 Validation error. UpdateRequest object is null. Specify a valid UpdateRequest object."), - UpdateRecordNull("%s0 Validation error. UpdateRequestRecord object in the list is null. Specify a valid UpdateRequestRecord object."), - RecordSkyflowIdKeyError("%s0 Validation error. 'skyflowId' key is missing from the record. Specify a 'skyflowId' key."), - EmptySkyflowIdInRecord("%s0 Validation error. 'skyflowId' can't be empty in the record. Specify a valid skyflow ID."), // Query QueryRequestNull("%s0 Validation error. QueryRequest object is null. Specify a valid QueryRequest object."), @@ -159,15 +129,6 @@ public enum ErrorMessage { ColumnValuesKeyErrorTokenize("%s0 Validation error. 'columnValues' key is missing from the payload. Specify a 'columnValues' key."), EmptyColumnGroupInColumnValue("%s0 Validation error. Invalid column group in column value. Specify a valid column group."), TokenizeRequestNull("%s0 Validation error. TokenizeRequest object is null. Specify a valid TokenizeRequest object."), - EmptyTokenizeData("%s0 Validation error. Tokenize data is empty. Specify at least one tokenize record."), - TokenizeRecordNull("%s0 Validation error. TokenizeRecord in the list is null. Specify a valid TokenizeRecord object."), - InvalidBulkTokenizeRecordType("%s0 Validation error. Record in the list must be of type BulkTokenizeRequestRecord. Specify a valid BulkTokenizeRequestRecord object."), - EmptyValueInTokenizeRecord("%s0 Validation error. Value in TokenizeRecord is null or empty. Specify a valid value."), - EmptyTokenGroupNamesInTokenizeRecord("%s0 Validation error. TokenGroupNames in TokenizeRecord is null or empty. Specify at least one token group name."), - EmptyTokenGroupNameInTokenizeRecord("%s0 Validation error. Token group name in TokenizeRecord is null or empty. Specify a valid token group name."), - TokenizeDataSizeExceedError("%s0 Maximum number of tokenize records exceeded. The limit is 100000."), - MissingIndexInBulkTokenizeRecord("%s0 Validation error. Index in BulkTokenizeRequestRecord is null. Specify an index for every record."), - DuplicateIndexInBulkTokenizeRecord("%s0 Validation error. Duplicate index in BulkTokenizeRequestRecord. Specify a unique index for every record."), // Connection InvalidRequestHeaders("%s0 Validation error. Request headers aren't valid. Specify valid request headers."), @@ -215,17 +176,30 @@ public enum ErrorMessage { DetokenizeRequestNull("%s0 Validation error. DetokenizeRequest object is null. Specify a valid DetokenizeRequest object."), - NullTokenGroupRedactions("%s0 Validation error. TokenGroupRedaction in the list is null. Specify a valid TokenGroupRedactions object."), - NullRedactionInTokenGroup("%s0 Validation error. Redaction in TokenGroupRedactions is null or empty. Specify a valid redaction."), - NullTokenGroupNameInTokenGroup("%s0 Validation error. TokenGroupName in TokenGroupRedactions is null or empty. Specify a valid tokenGroupName."), - InvalidRecord("%s0 Validation error. InsertRecord object in the list is invalid. Specify a valid InsertRecord object."), + // Detect + DeidentifyStringRequestNull("%s0 Validation error. DeidentifyString request cannot be null."), + InvalidTextInDeidentifyString("%s0 Validation error. Invalid text in deidentify string request. Specify a non-empty text."), + ReidentifyStringRequestNull("%s0 Validation error. ReidentifyString request cannot be null."), + InvalidTextInReidentifyString("%s0 Validation error. Invalid text in reidentify string request. Specify a non-empty text."), + DeidentifyFileRequestNull("%s0 Validation error. DeidentifyFile request cannot be null."), + InvalidDataSourceInDeidentifyFile("%s0 Validation error. Invalid dataSource in deidentify file request. Specify BASE64, SKYFLOW_ID or PRESIGNED_URL, or supply a file."), + InvalidValueInDeidentifyFile("%s0 Validation error. Invalid value in deidentify file request. Specify the non-empty base64 content, skyflow id or presigned URL of the file, or supply a file."), + MultipleFileSourcesInDeidentifyFile("%s0 Validation error. Specify either value or file in deidentify file request, not both."), + InvalidWaitTimeInPollOptions("%s0 Validation error. waitTime in pollOptions must be between 1 and 300 seconds."), + InvalidMaxAttemptsInPollOptions("%s0 Validation error. maxAttempts in pollOptions must be between 1 and 23."), + OutputDirectoryWithoutPollOptions("%s0 Validation error. outputDirectory in deidentify file request requires pollOptions."), + FailedToWriteOutputFile("%s0 Failed to write the processed output to %s1."), + GetRunRequestNull("%s0 Validation error. GetRun request cannot be null."), + InvalidRunIdInGetRun("%s0 Validation error. Invalid runId in get run request. Specify a non-empty run id."), + CheckGuardrailsRequestNull("%s0 Validation error. CheckGuardrails request cannot be null."), + InvalidTextInCheckGuardrails("%s0 Validation error. Invalid text in check guardrails request. Specify a non-empty text."), ; private final String message; - ErrorMessage(String message) { + BaseErrorMessage(String message) { this.message = message; } diff --git a/common/src/main/java/com/skyflow/errors/SkyflowException.java b/common/src/main/java/com/skyflow/errors/SkyflowException.java index 2b043f92..f8c595ba 100644 --- a/common/src/main/java/com/skyflow/errors/SkyflowException.java +++ b/common/src/main/java/com/skyflow/errors/SkyflowException.java @@ -89,7 +89,7 @@ public SkyflowException(int httpCode, Throwable cause, Map> } catch (Exception e) { this.httpStatus = HttpStatus.BAD_REQUEST.getHttpStatus(); String fullMessage = responseBody != null ? responseBody : - (cause.getLocalizedMessage() != null ? cause.getMessage() : ErrorMessage.ErrorOccurred.getMessage()); + (cause.getLocalizedMessage() != null ? cause.getMessage() : BaseErrorMessage.ErrorOccurred.getMessage()); this.message = fullMessage.split("HTTP response code:")[0].trim(); } } diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/core/ApiClientApiException.java b/common/src/main/java/com/skyflow/generated/detect/rest/core/ApiClientApiException.java new file mode 100644 index 00000000..d864c404 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/core/ApiClientApiException.java @@ -0,0 +1,73 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.core; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import okhttp3.Response; + +/** + * This exception type will be thrown for any non-2XX API responses. + */ +public class ApiClientApiException extends ApiClientException { + /** + * The error code of the response that triggered the exception. + */ + private final int statusCode; + + /** + * The body of the response that triggered the exception. + */ + private final Object body; + + private final Map> headers; + + public ApiClientApiException(String message, int statusCode, Object body) { + super(message); + this.statusCode = statusCode; + this.body = body; + this.headers = new HashMap<>(); + } + + public ApiClientApiException(String message, int statusCode, Object body, Response rawResponse) { + super(message); + this.statusCode = statusCode; + this.body = body; + this.headers = new HashMap<>(); + rawResponse.headers().forEach(header -> { + String key = header.component1(); + String value = header.component2(); + this.headers.computeIfAbsent(key, _str -> new ArrayList<>()).add(value); + }); + } + + /** + * @return the statusCode + */ + public int statusCode() { + return this.statusCode; + } + + /** + * @return the body + */ + public Object body() { + return this.body; + } + + /** + * @return the headers + */ + public Map> headers() { + return this.headers; + } + + @Override + public String toString() { + return "ApiClientApiException{" + "message: " + getMessage() + ", statusCode: " + statusCode + ", body: " + + ObjectMappers.stringify(body) + "}"; + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/core/ApiClientException.java b/common/src/main/java/com/skyflow/generated/detect/rest/core/ApiClientException.java new file mode 100644 index 00000000..0f99fbc6 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/core/ApiClientException.java @@ -0,0 +1,17 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.core; + +/** + * This class serves as the base exception for all errors in the SDK. + */ +public class ApiClientException extends RuntimeException { + public ApiClientException(String message) { + super(message); + } + + public ApiClientException(String message, Exception e) { + super(message, e); + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/core/ApiClientHttpResponse.java b/common/src/main/java/com/skyflow/generated/detect/rest/core/ApiClientHttpResponse.java new file mode 100644 index 00000000..4b251a98 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/core/ApiClientHttpResponse.java @@ -0,0 +1,37 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.core; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import okhttp3.Response; + +public final class ApiClientHttpResponse { + + private final T body; + + private final Map> headers; + + public ApiClientHttpResponse(T body, Response rawResponse) { + this.body = body; + + Map> headers = new HashMap<>(); + rawResponse.headers().forEach(header -> { + String key = header.component1(); + String value = header.component2(); + headers.computeIfAbsent(key, _str -> new ArrayList<>()).add(value); + }); + this.headers = headers; + } + + public T body() { + return this.body; + } + + public Map> headers() { + return headers; + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/core/ClientOptions.java b/common/src/main/java/com/skyflow/generated/detect/rest/core/ClientOptions.java new file mode 100644 index 00000000..cf61d5db --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/core/ClientOptions.java @@ -0,0 +1,320 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.core; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; +import okhttp3.OkHttpClient; + +public final class ClientOptions { + private final Environment environment; + + private final Map headers; + + private final Map> headerSuppliers; + + private final OkHttpClient httpClient; + + private final boolean ownsHttpClient; + + private final int timeout; + + private final int maxRetries; + + private final Optional initialRetryDelayMillis; + + private final Optional maxRetryDelayMillis; + + private final Optional retryJitterFactor; + + private final Optional logging; + + private ClientOptions( + Environment environment, + Map headers, + Map> headerSuppliers, + OkHttpClient httpClient, + boolean ownsHttpClient, + int timeout, + int maxRetries, + Optional initialRetryDelayMillis, + Optional maxRetryDelayMillis, + Optional retryJitterFactor, + Optional logging) { + this.environment = environment; + this.headers = new HashMap<>(); + this.headers.putAll(headers); + this.headers.putAll(new HashMap() { + { + put("X-Fern-Language", "JAVA"); + put("X-Fern-SDK-Name", "com.skyflow.fern:api-sdk"); + put("X-Fern-SDK-Version", "0.0.98"); + } + }); + this.headerSuppliers = headerSuppliers; + this.httpClient = httpClient; + this.ownsHttpClient = ownsHttpClient; + this.timeout = timeout; + this.maxRetries = maxRetries; + this.initialRetryDelayMillis = initialRetryDelayMillis; + this.maxRetryDelayMillis = maxRetryDelayMillis; + this.retryJitterFactor = retryJitterFactor; + this.logging = logging; + } + + public Environment environment() { + return this.environment; + } + + public Map headers(RequestOptions requestOptions) { + Map values = new HashMap<>(this.headers); + headerSuppliers.forEach((key, supplier) -> { + values.put(key, supplier.get()); + }); + if (requestOptions != null) { + values.putAll(requestOptions.getHeaders()); + } + return values; + } + + public int timeout(RequestOptions requestOptions) { + if (requestOptions == null) { + return this.timeout; + } + return requestOptions.getTimeout().orElse(this.timeout); + } + + public OkHttpClient httpClient() { + return this.httpClient; + } + + public OkHttpClient httpClientWithTimeout(RequestOptions requestOptions) { + if (requestOptions == null) { + return this.httpClient; + } + return this.httpClient + .newBuilder() + .callTimeout(requestOptions.getTimeout().get(), requestOptions.getTimeoutTimeUnit()) + .connectTimeout(0, TimeUnit.SECONDS) + .writeTimeout(0, TimeUnit.SECONDS) + .readTimeout(0, TimeUnit.SECONDS) + .build(); + } + + public int maxRetries() { + return this.maxRetries; + } + + public Optional initialRetryDelayMillis() { + return this.initialRetryDelayMillis; + } + + public Optional maxRetryDelayMillis() { + return this.maxRetryDelayMillis; + } + + public Optional retryJitterFactor() { + return this.retryJitterFactor; + } + + /** + * Releases resources owned by this client. Only shuts down the underlying OkHttpClient's + * dispatcher executor and evicts its connection pool when this client created that + * OkHttpClient itself; an OkHttpClient supplied via httpClient is left running, since the + * caller owns its lifecycle. + *

+ * In-flight calls are not cancelled or awaited, and any request issued after this method + * returns fails with a {@code RejectedExecutionException}. Options derived from this one via + * {@code Builder.from(...)} share the same dispatcher and connection pool, so closing either + * releases them for both. Calling this method more than once has no further effect. + */ + public void close() { + if (!this.ownsHttpClient) { + return; + } + this.httpClient.dispatcher().executorService().shutdown(); + this.httpClient.connectionPool().evictAll(); + } + + public Optional logging() { + return this.logging; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private Environment environment; + + private final Map headers = new HashMap<>(); + + private final Map> headerSuppliers = new HashMap<>(); + + private int maxRetries = 2; + + private Optional initialRetryDelayMillis = Optional.empty(); + + private Optional maxRetryDelayMillis = Optional.empty(); + + private Optional retryJitterFactor = Optional.empty(); + + private Optional timeout = Optional.empty(); + + private OkHttpClient httpClient = null; + + private boolean ownsHttpClient = true; + + private Optional logging = Optional.empty(); + + public Builder environment(Environment environment) { + this.environment = environment; + return this; + } + + public Builder addHeader(String key, String value) { + if (value != null) { + this.headers.put(key, value); + } + return this; + } + + public Builder addHeader(String key, Supplier value) { + this.headerSuppliers.put(key, value); + return this; + } + + /** + * Override the timeout in seconds. Defaults to 60 seconds. + */ + public Builder timeout(int timeout) { + this.timeout = Optional.of(timeout); + return this; + } + + /** + * Override the timeout in seconds. Defaults to 60 seconds. + */ + public Builder timeout(Optional timeout) { + this.timeout = timeout; + return this; + } + + /** + * Override the maximum number of retries. Defaults to 2 retries. + */ + public Builder maxRetries(int maxRetries) { + this.maxRetries = maxRetries; + return this; + } + + /** + * Override the initial delay (in milliseconds) used for exponential backoff between retries. Defaults to 1000 milliseconds. + */ + public Builder initialRetryDelayMillis(long initialRetryDelayMillis) { + this.initialRetryDelayMillis = Optional.of(initialRetryDelayMillis); + return this; + } + + /** + * Override the maximum delay (in milliseconds) between retries. Defaults to 60000 milliseconds. + */ + public Builder maxRetryDelayMillis(long maxRetryDelayMillis) { + this.maxRetryDelayMillis = Optional.of(maxRetryDelayMillis); + return this; + } + + /** + * Override the jitter factor (between 0 and 1) applied to retry delays. Defaults to 0.2. + */ + public Builder retryJitterFactor(double retryJitterFactor) { + this.retryJitterFactor = Optional.of(retryJitterFactor); + return this; + } + + /** + * Sets the underlying OkHttp client. The caller retains ownership of its lifecycle: + * close() will not shut down its dispatcher executor or evict its connection pool. + */ + public Builder httpClient(OkHttpClient httpClient) { + this.httpClient = httpClient; + this.ownsHttpClient = httpClient == null; + return this; + } + + /** + * Configure logging for the SDK. Silent by default — no log output unless explicitly configured. + */ + public Builder logging(LogConfig logging) { + this.logging = Optional.of(logging); + return this; + } + + public ClientOptions build() { + OkHttpClient.Builder httpClientBuilder = + this.httpClient != null ? this.httpClient.newBuilder() : new OkHttpClient.Builder(); + + if (this.httpClient != null) { + timeout.ifPresent(timeout -> httpClientBuilder + .callTimeout(timeout, TimeUnit.SECONDS) + .connectTimeout(0, TimeUnit.SECONDS) + .writeTimeout(0, TimeUnit.SECONDS) + .readTimeout(0, TimeUnit.SECONDS)); + } else { + httpClientBuilder + .callTimeout(this.timeout.orElse(60), TimeUnit.SECONDS) + .connectTimeout(0, TimeUnit.SECONDS) + .writeTimeout(0, TimeUnit.SECONDS) + .readTimeout(0, TimeUnit.SECONDS) + .addInterceptor(new RetryInterceptor( + this.maxRetries, + this.initialRetryDelayMillis, + this.maxRetryDelayMillis, + this.retryJitterFactor)); + } + + Logger logger = Logger.from(this.logging); + httpClientBuilder.addInterceptor(new LoggingInterceptor(logger)); + httpClientBuilder.addInterceptor(new ResponseDecompressionInterceptor()); + + this.httpClient = httpClientBuilder.build(); + this.timeout = Optional.of(httpClient.callTimeoutMillis() / 1000); + + return new ClientOptions( + environment, + headers, + headerSuppliers, + httpClient, + this.ownsHttpClient, + this.timeout.get(), + this.maxRetries, + this.initialRetryDelayMillis, + this.maxRetryDelayMillis, + this.retryJitterFactor, + this.logging); + } + + /** + * Create a new Builder initialized with values from an existing ClientOptions + */ + public static Builder from(ClientOptions clientOptions) { + Builder builder = new Builder(); + builder.environment = clientOptions.environment(); + builder.timeout = Optional.of(clientOptions.timeout(null)); + builder.httpClient = clientOptions.httpClient(); + builder.ownsHttpClient = clientOptions.ownsHttpClient; + builder.headers.putAll(clientOptions.headers); + builder.headerSuppliers.putAll(clientOptions.headerSuppliers); + builder.maxRetries = clientOptions.maxRetries(); + builder.initialRetryDelayMillis = clientOptions.initialRetryDelayMillis(); + builder.maxRetryDelayMillis = clientOptions.maxRetryDelayMillis(); + builder.retryJitterFactor = clientOptions.retryJitterFactor(); + builder.logging = clientOptions.logging(); + return builder; + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/core/ConsoleLogger.java b/common/src/main/java/com/skyflow/generated/detect/rest/core/ConsoleLogger.java new file mode 100644 index 00000000..e4feb843 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/core/ConsoleLogger.java @@ -0,0 +1,52 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.core; + +import java.util.logging.Level; + +/** + * Default logger implementation that writes to the console using {@link java.util.logging.Logger}. + * + *

Uses the "fern" logger name with a simple format of "LEVEL - message". + */ +public final class ConsoleLogger implements ILogger { + + private static final java.util.logging.Logger logger = java.util.logging.Logger.getLogger("fern"); + + static { + if (logger.getHandlers().length == 0) { + java.util.logging.ConsoleHandler handler = new java.util.logging.ConsoleHandler(); + handler.setFormatter(new java.util.logging.SimpleFormatter() { + @Override + public String format(java.util.logging.LogRecord record) { + return record.getLevel() + " - " + record.getMessage() + System.lineSeparator(); + } + }); + handler.setLevel(Level.ALL); + logger.addHandler(handler); + logger.setUseParentHandlers(false); + logger.setLevel(Level.ALL); + } + } + + @Override + public void debug(String message) { + logger.log(Level.FINE, message); + } + + @Override + public void info(String message) { + logger.log(Level.INFO, message); + } + + @Override + public void warn(String message) { + logger.log(Level.WARNING, message); + } + + @Override + public void error(String message) { + logger.log(Level.SEVERE, message); + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/core/DateTimeDeserializer.java b/common/src/main/java/com/skyflow/generated/detect/rest/core/DateTimeDeserializer.java new file mode 100644 index 00000000..f2c9053a --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/core/DateTimeDeserializer.java @@ -0,0 +1,70 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.core; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.module.SimpleModule; +import java.io.IOException; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalQueries; + +/** + * Custom deserializer that handles converting date-time strings into {@link OffsetDateTime} objects. + * Supports ISO 8601 format, space-separated variants, and RFC 1123 (RFC 2822) format. + */ +class DateTimeDeserializer extends JsonDeserializer { + private static final SimpleModule MODULE; + + static { + MODULE = new SimpleModule().addDeserializer(OffsetDateTime.class, new DateTimeDeserializer()); + } + + /** + * Gets a module wrapping this deserializer as an adapter for the Jackson ObjectMapper. + * + * @return A {@link SimpleModule} to be plugged onto Jackson ObjectMapper. + */ + public static SimpleModule getModule() { + return MODULE; + } + + @Override + public OffsetDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException { + JsonToken token = parser.currentToken(); + if (token == JsonToken.VALUE_NUMBER_INT) { + return OffsetDateTime.ofInstant(Instant.ofEpochSecond(parser.getValueAsLong()), ZoneOffset.UTC); + } else { + String value = parser.getValueAsString(); + TemporalAccessor temporal; + try { + temporal = DateTimeFormatter.ISO_DATE_TIME.parseBest(value, OffsetDateTime::from, LocalDateTime::from); + } catch (DateTimeParseException e) { + try { + // Fall back to space-separated format (e.g. "2025-02-15 10:30:00+00:00"). + temporal = DateTimeFormatter.ISO_DATE_TIME.parseBest( + value.replace(' ', 'T'), OffsetDateTime::from, LocalDateTime::from); + } catch (DateTimeParseException e2) { + // Fall back to RFC 1123 format (e.g. "Thu, 07 May 2026 14:23:38 +0000"). + temporal = DateTimeFormatter.RFC_1123_DATE_TIME.parseBest( + value, OffsetDateTime::from, LocalDateTime::from); + } + } + + if (temporal.query(TemporalQueries.offset()) == null) { + return LocalDateTime.from(temporal).atOffset(ZoneOffset.UTC); + } else { + return OffsetDateTime.from(temporal); + } + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/core/DoubleSerializer.java b/common/src/main/java/com/skyflow/generated/detect/rest/core/DoubleSerializer.java new file mode 100644 index 00000000..635869a6 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/core/DoubleSerializer.java @@ -0,0 +1,43 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.core; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.module.SimpleModule; +import java.io.IOException; + +/** + * Custom serializer that writes integer-valued doubles without a decimal point. + * For example, {@code 24000.0} is serialized as {@code 24000} instead of {@code 24000.0}. + * Non-integer values like {@code 3.14} are serialized normally. + */ +class DoubleSerializer extends JsonSerializer { + private static final SimpleModule MODULE; + + static { + MODULE = new SimpleModule() + .addSerializer(Double.class, new DoubleSerializer()) + .addSerializer(double.class, new DoubleSerializer()); + } + + /** + * Gets a module wrapping this serializer as an adapter for the Jackson ObjectMapper. + * + * @return A {@link SimpleModule} to be plugged onto Jackson ObjectMapper. + */ + public static SimpleModule getModule() { + return MODULE; + } + + @Override + public void serialize(Double value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + if (value != null && value == Math.floor(value) && !Double.isInfinite(value) && !Double.isNaN(value)) { + gen.writeNumber(value.longValue()); + } else { + gen.writeNumber(value); + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/core/Environment.java b/common/src/main/java/com/skyflow/generated/detect/rest/core/Environment.java new file mode 100644 index 00000000..c95311b0 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/core/Environment.java @@ -0,0 +1,25 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.core; + +public final class Environment { + public static final Environment PRODUCTION = new Environment("https://%7B%7BvaultUri%7D%7D.vault.skyflowapis.com"); + + public static final Environment SANDBOX = + new Environment("https://%7B%7BvaultUri%7D%7D.vault.skyflowapis-preview.com"); + + private final String url; + + private Environment(String url) { + this.url = url; + } + + public String getUrl() { + return this.url; + } + + public static Environment custom(String url) { + return new Environment(url); + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/core/ILogger.java b/common/src/main/java/com/skyflow/generated/detect/rest/core/ILogger.java new file mode 100644 index 00000000..0df7a4f5 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/core/ILogger.java @@ -0,0 +1,38 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.core; + +/** + * Interface for custom logger implementations. + * + *

Implement this interface to provide a custom logging backend for the SDK. + * The SDK will call the appropriate method based on the log level. + * + *

Example: + *

{@code
+ * public class MyCustomLogger implements ILogger {
+ *     public void debug(String message) {
+ *         System.out.println("[DBG] " + message);
+ *     }
+ *     public void info(String message) {
+ *         System.out.println("[INF] " + message);
+ *     }
+ *     public void warn(String message) {
+ *         System.out.println("[WRN] " + message);
+ *     }
+ *     public void error(String message) {
+ *         System.out.println("[ERR] " + message);
+ *     }
+ * }
+ * }
+ */ +public interface ILogger { + void debug(String message); + + void info(String message); + + void warn(String message); + + void error(String message); +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/core/LogConfig.java b/common/src/main/java/com/skyflow/generated/detect/rest/core/LogConfig.java new file mode 100644 index 00000000..e613616b --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/core/LogConfig.java @@ -0,0 +1,98 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.core; + +/** + * Configuration for SDK logging. + * + *

Use the builder to configure logging behavior: + *

{@code
+ * LogConfig config = LogConfig.builder()
+ *     .level(LogLevel.DEBUG)
+ *     .silent(false)
+ *     .build();
+ * }
+ * + *

Or with a custom logger: + *

{@code
+ * LogConfig config = LogConfig.builder()
+ *     .level(LogLevel.DEBUG)
+ *     .logger(new MyCustomLogger())
+ *     .silent(false)
+ *     .build();
+ * }
+ * + *

Defaults: + *

    + *
  • {@code level} — {@link LogLevel#INFO}
  • + *
  • {@code logger} — {@link ConsoleLogger} (writes to stderr via java.util.logging)
  • + *
  • {@code silent} — {@code true} (no output unless explicitly enabled)
  • + *
+ */ +public final class LogConfig { + + private final LogLevel level; + private final ILogger logger; + private final boolean silent; + + private LogConfig(LogLevel level, ILogger logger, boolean silent) { + this.level = level; + this.logger = logger; + this.silent = silent; + } + + public LogLevel level() { + return level; + } + + public ILogger logger() { + return logger; + } + + public boolean silent() { + return silent; + } + + public static Builder builder() { + return new Builder(); + } + + public static final class Builder { + private LogLevel level = LogLevel.INFO; + private ILogger logger = new ConsoleLogger(); + private boolean silent = true; + + private Builder() {} + + /** + * Set the minimum log level. Only messages at this level or above will be logged. + * Defaults to {@link LogLevel#INFO}. + */ + public Builder level(LogLevel level) { + this.level = level; + return this; + } + + /** + * Set a custom logger implementation. Defaults to {@link ConsoleLogger}. + */ + public Builder logger(ILogger logger) { + this.logger = logger; + return this; + } + + /** + * Set whether logging is silent (disabled). Defaults to {@code true}. + * Set to {@code false} to enable log output. + */ + public Builder silent(boolean silent) { + this.silent = silent; + return this; + } + + public LogConfig build() { + return new LogConfig(level, logger, silent); + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/core/LogLevel.java b/common/src/main/java/com/skyflow/generated/detect/rest/core/LogLevel.java new file mode 100644 index 00000000..b37806c0 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/core/LogLevel.java @@ -0,0 +1,36 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.core; + +/** + * Log levels for SDK logging configuration. + * Silent by default — no log output unless explicitly configured. + */ +public enum LogLevel { + DEBUG(1), + INFO(2), + WARN(3), + ERROR(4); + + private final int value; + + LogLevel(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + /** + * Parse a log level from a string (case-insensitive). + * + * @param level the level string (debug, info, warn, error) + * @return the corresponding LogLevel + * @throws IllegalArgumentException if the string does not match any level + */ + public static LogLevel fromString(String level) { + return LogLevel.valueOf(level.toUpperCase()); + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/core/Logger.java b/common/src/main/java/com/skyflow/generated/detect/rest/core/Logger.java new file mode 100644 index 00000000..19a9d1fe --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/core/Logger.java @@ -0,0 +1,97 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.core; + +/** + * SDK logger that filters messages based on level and silent mode. + * + *

Silent by default — no log output unless explicitly configured. + * Create via {@link LogConfig} or directly: + *

{@code
+ * Logger logger = new Logger(LogLevel.DEBUG, new ConsoleLogger(), false);
+ * logger.debug("request sent");
+ * }
+ */ +public final class Logger { + + private static final Logger DEFAULT = new Logger(LogLevel.INFO, new ConsoleLogger(), true); + + private final LogLevel level; + private final ILogger logger; + private final boolean silent; + + public Logger(LogLevel level, ILogger logger, boolean silent) { + this.level = level; + this.logger = logger; + this.silent = silent; + } + + /** + * Returns a default silent logger (no output). + */ + public static Logger getDefault() { + return DEFAULT; + } + + /** + * Creates a Logger from a {@link LogConfig}. If config is {@code null}, returns the default silent logger. + */ + public static Logger from(LogConfig config) { + if (config == null) { + return DEFAULT; + } + return new Logger(config.level(), config.logger(), config.silent()); + } + + /** + * Creates a Logger from an {@code Optional}. If empty, returns the default silent logger. + */ + public static Logger from(java.util.Optional config) { + return config.map(Logger::from).orElse(DEFAULT); + } + + private boolean shouldLog(LogLevel messageLevel) { + return !silent && level.getValue() <= messageLevel.getValue(); + } + + public boolean isDebug() { + return shouldLog(LogLevel.DEBUG); + } + + public boolean isInfo() { + return shouldLog(LogLevel.INFO); + } + + public boolean isWarn() { + return shouldLog(LogLevel.WARN); + } + + public boolean isError() { + return shouldLog(LogLevel.ERROR); + } + + public void debug(String message) { + if (isDebug()) { + logger.debug(message); + } + } + + public void info(String message) { + if (isInfo()) { + logger.info(message); + } + } + + public void warn(String message) { + if (isWarn()) { + logger.warn(message); + } + } + + public void error(String message) { + if (isError()) { + logger.error(message); + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/core/LoggingInterceptor.java b/common/src/main/java/com/skyflow/generated/detect/rest/core/LoggingInterceptor.java new file mode 100644 index 00000000..33c1d6aa --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/core/LoggingInterceptor.java @@ -0,0 +1,104 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.core; + +import java.io.IOException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import okhttp3.Interceptor; +import okhttp3.Request; +import okhttp3.Response; + +/** + * OkHttp interceptor that logs HTTP requests and responses. + * + *

Logs request method, URL, and headers (with sensitive values redacted) at debug level. + * Logs response status at debug level, and 4xx/5xx responses at error level. + * Does nothing if the logger is silent. + */ +public final class LoggingInterceptor implements Interceptor { + + private static final Set SENSITIVE_HEADERS = new HashSet<>(Arrays.asList( + "authorization", + "www-authenticate", + "x-api-key", + "api-key", + "apikey", + "x-api-token", + "x-auth-token", + "auth-token", + "proxy-authenticate", + "proxy-authorization", + "cookie", + "set-cookie", + "x-csrf-token", + "x-xsrf-token", + "x-session-token", + "x-access-token")); + + private final Logger logger; + + public LoggingInterceptor(Logger logger) { + this.logger = logger; + } + + @Override + public Response intercept(Chain chain) throws IOException { + Request request = chain.request(); + + if (logger.isDebug()) { + StringBuilder sb = new StringBuilder(); + sb.append("HTTP Request: ").append(request.method()).append(" ").append(request.url()); + sb.append(" headers={"); + boolean first = true; + for (String name : request.headers().names()) { + if (!first) { + sb.append(", "); + } + sb.append(name).append("="); + if (SENSITIVE_HEADERS.contains(name.toLowerCase())) { + sb.append("[REDACTED]"); + } else { + sb.append(request.header(name)); + } + first = false; + } + sb.append("}"); + sb.append(" has_body=").append(request.body() != null); + logger.debug(sb.toString()); + } + + Response response = chain.proceed(request); + + if (logger.isDebug()) { + StringBuilder sb = new StringBuilder(); + sb.append("HTTP Response: status=").append(response.code()); + sb.append(" url=").append(response.request().url()); + sb.append(" headers={"); + boolean first = true; + for (String name : response.headers().names()) { + if (!first) { + sb.append(", "); + } + sb.append(name).append("="); + if (SENSITIVE_HEADERS.contains(name.toLowerCase())) { + sb.append("[REDACTED]"); + } else { + sb.append(response.header(name)); + } + first = false; + } + sb.append("}"); + logger.debug(sb.toString()); + } + + if (response.code() >= 400 && logger.isError()) { + logger.error("HTTP Error: status=" + response.code() + " url=" + + response.request().url()); + } + + return response; + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/core/MediaTypes.java b/common/src/main/java/com/skyflow/generated/detect/rest/core/MediaTypes.java new file mode 100644 index 00000000..b2d084be --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/core/MediaTypes.java @@ -0,0 +1,13 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.core; + +import okhttp3.MediaType; + +public final class MediaTypes { + + public static final MediaType APPLICATION_JSON = MediaType.parse("application/json"); + + private MediaTypes() {} +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/core/ObjectMappers.java b/common/src/main/java/com/skyflow/generated/detect/rest/core/ObjectMappers.java new file mode 100644 index 00000000..7b4660ad --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/core/ObjectMappers.java @@ -0,0 +1,46 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.core; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import java.io.IOException; + +public final class ObjectMappers { + public static final ObjectMapper JSON_MAPPER = JsonMapper.builder() + .addModule(new Jdk8Module()) + .addModule(new JavaTimeModule()) + .addModule(DateTimeDeserializer.getModule()) + .addModule(DoubleSerializer.getModule()) + .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .build(); + + private ObjectMappers() {} + + public static String stringify(Object o) { + try { + return JSON_MAPPER + .setSerializationInclusion(JsonInclude.Include.ALWAYS) + .writerWithDefaultPrettyPrinter() + .writeValueAsString(o); + } catch (IOException e) { + return o.getClass().getName() + "@" + Integer.toHexString(o.hashCode()); + } + } + + public static Object parseErrorBody(String responseBodyString) { + try { + return JSON_MAPPER.readValue(responseBodyString, Object.class); + } catch (JsonProcessingException ignored) { + return responseBodyString; + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/core/QueryStringMapper.java b/common/src/main/java/com/skyflow/generated/detect/rest/core/QueryStringMapper.java new file mode 100644 index 00000000..ba2dadc4 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/core/QueryStringMapper.java @@ -0,0 +1,142 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.core; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import okhttp3.HttpUrl; +import okhttp3.MultipartBody; + +public class QueryStringMapper { + + private static final ObjectMapper MAPPER = ObjectMappers.JSON_MAPPER; + + public static void addQueryParameter(HttpUrl.Builder httpUrl, String key, Object value, boolean arraysAsRepeats) { + JsonNode valueNode = MAPPER.valueToTree(value); + + List> flat; + if (valueNode.isObject()) { + flat = flattenObject((ObjectNode) valueNode, arraysAsRepeats); + } else if (valueNode.isArray()) { + flat = flattenArray((ArrayNode) valueNode, "", arraysAsRepeats); + } else { + if (valueNode.isTextual()) { + httpUrl.addQueryParameter(key, valueNode.textValue()); + } else { + httpUrl.addQueryParameter(key, valueNode.toString()); + } + return; + } + + for (Map.Entry field : flat) { + if (field.getValue().isTextual()) { + httpUrl.addQueryParameter(key + field.getKey(), field.getValue().textValue()); + } else { + httpUrl.addQueryParameter(key + field.getKey(), field.getValue().toString()); + } + } + } + + public static void addFormDataPart( + MultipartBody.Builder multipartBody, String key, Object value, boolean arraysAsRepeats) { + JsonNode valueNode = MAPPER.valueToTree(value); + + List> flat; + if (valueNode.isObject()) { + flat = flattenObject((ObjectNode) valueNode, arraysAsRepeats); + } else if (valueNode.isArray()) { + flat = flattenArray((ArrayNode) valueNode, "", arraysAsRepeats); + } else { + if (valueNode.isTextual()) { + multipartBody.addFormDataPart(key, valueNode.textValue()); + } else { + multipartBody.addFormDataPart(key, valueNode.toString()); + } + return; + } + + for (Map.Entry field : flat) { + if (field.getValue().isTextual()) { + multipartBody.addFormDataPart( + key + field.getKey(), field.getValue().textValue()); + } else { + multipartBody.addFormDataPart( + key + field.getKey(), field.getValue().toString()); + } + } + } + + public static List> flattenObject(ObjectNode object, boolean arraysAsRepeats) { + List> flat = new ArrayList<>(); + + Iterator> fields = object.fields(); + while (fields.hasNext()) { + Map.Entry field = fields.next(); + + String key = "[" + field.getKey() + "]"; + + if (field.getValue().isObject()) { + List> flatField = + flattenObject((ObjectNode) field.getValue(), arraysAsRepeats); + addAll(flat, flatField, key); + } else if (field.getValue().isArray()) { + List> flatField = + flattenArray((ArrayNode) field.getValue(), key, arraysAsRepeats); + addAll(flat, flatField, ""); + } else { + flat.add(new AbstractMap.SimpleEntry<>(key, field.getValue())); + } + } + + return flat; + } + + private static List> flattenArray( + ArrayNode array, String key, boolean arraysAsRepeats) { + List> flat = new ArrayList<>(); + + Iterator elements = array.elements(); + + int index = 0; + while (elements.hasNext()) { + JsonNode element = elements.next(); + + String indexKey = key + "[" + index + "]"; + + if (arraysAsRepeats) { + indexKey = key; + } + + if (element.isObject()) { + List> flatField = flattenObject((ObjectNode) element, arraysAsRepeats); + addAll(flat, flatField, indexKey); + } else if (element.isArray()) { + List> flatField = flattenArray((ArrayNode) element, "", arraysAsRepeats); + addAll(flat, flatField, indexKey); + } else { + flat.add(new AbstractMap.SimpleEntry<>(indexKey, element)); + } + + index++; + } + + return flat; + } + + private static void addAll( + List> target, List> source, String prefix) { + for (Map.Entry entry : source) { + Map.Entry entryToAdd = + new AbstractMap.SimpleEntry<>(prefix + entry.getKey(), entry.getValue()); + target.add(entryToAdd); + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/core/RequestOptions.java b/common/src/main/java/com/skyflow/generated/detect/rest/core/RequestOptions.java new file mode 100644 index 00000000..7f5ccea0 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/core/RequestOptions.java @@ -0,0 +1,154 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.core; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; + +public final class RequestOptions { + private final String token; + + private final Optional timeout; + + private final TimeUnit timeoutTimeUnit; + + private final Optional maxRetries; + + private final Map headers; + + private final Map> headerSuppliers; + + private final Map queryParameters; + + private final Map> queryParameterSuppliers; + + private RequestOptions( + String token, + Optional timeout, + TimeUnit timeoutTimeUnit, + Optional maxRetries, + Map headers, + Map> headerSuppliers, + Map queryParameters, + Map> queryParameterSuppliers) { + this.token = token; + this.timeout = timeout; + this.timeoutTimeUnit = timeoutTimeUnit; + this.maxRetries = maxRetries; + this.headers = headers; + this.headerSuppliers = headerSuppliers; + this.queryParameters = queryParameters; + this.queryParameterSuppliers = queryParameterSuppliers; + } + + public Optional getTimeout() { + return timeout; + } + + public TimeUnit getTimeoutTimeUnit() { + return timeoutTimeUnit; + } + + public Optional getMaxRetries() { + return maxRetries; + } + + public Map getHeaders() { + Map headers = new HashMap<>(); + if (this.token != null) { + headers.put("Authorization", "Bearer " + this.token); + } + headers.putAll(this.headers); + this.headerSuppliers.forEach((key, supplier) -> { + headers.put(key, supplier.get()); + }); + return headers; + } + + public Map getQueryParameters() { + Map queryParameters = new HashMap<>(this.queryParameters); + this.queryParameterSuppliers.forEach((key, supplier) -> { + queryParameters.put(key, supplier.get()); + }); + return queryParameters; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private String token = null; + + private Optional timeout = Optional.empty(); + + private TimeUnit timeoutTimeUnit = TimeUnit.SECONDS; + + private Optional maxRetries = Optional.empty(); + + private final Map headers = new HashMap<>(); + + private final Map> headerSuppliers = new HashMap<>(); + + private final Map queryParameters = new HashMap<>(); + + private final Map> queryParameterSuppliers = new HashMap<>(); + + public Builder token(String token) { + this.token = token; + return this; + } + + public Builder timeout(Integer timeout) { + this.timeout = Optional.of(timeout); + return this; + } + + public Builder timeout(Integer timeout, TimeUnit timeoutTimeUnit) { + this.timeout = Optional.of(timeout); + this.timeoutTimeUnit = timeoutTimeUnit; + return this; + } + + public Builder maxRetries(Integer maxRetries) { + this.maxRetries = Optional.of(maxRetries); + return this; + } + + public Builder addHeader(String key, String value) { + this.headers.put(key, value); + return this; + } + + public Builder addHeader(String key, Supplier value) { + this.headerSuppliers.put(key, value); + return this; + } + + public Builder addQueryParameter(String key, String value) { + this.queryParameters.put(key, value); + return this; + } + + public Builder addQueryParameter(String key, Supplier value) { + this.queryParameterSuppliers.put(key, value); + return this; + } + + public RequestOptions build() { + return new RequestOptions( + token, + timeout, + timeoutTimeUnit, + maxRetries, + headers, + headerSuppliers, + queryParameters, + queryParameterSuppliers); + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/core/ResponseDecompressionInterceptor.java b/common/src/main/java/com/skyflow/generated/detect/rest/core/ResponseDecompressionInterceptor.java new file mode 100644 index 00000000..9988a7fc --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/core/ResponseDecompressionInterceptor.java @@ -0,0 +1,56 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.core; + +import java.io.IOException; +import java.util.zip.Inflater; +import okhttp3.Interceptor; +import okhttp3.Response; +import okhttp3.ResponseBody; +import okio.GzipSource; +import okio.InflaterSource; +import okio.Okio; +import okio.Source; + +/** + * Decompresses gzip and deflate encoded response bodies. OkHttp only performs + * transparent decompression when it adds the Accept-Encoding header itself, so + * responses to requests with an explicitly set Accept-Encoding header would + * otherwise be returned as raw compressed bytes. + */ +public class ResponseDecompressionInterceptor implements Interceptor { + + @Override + public Response intercept(Chain chain) throws IOException { + return decompress(chain.proceed(chain.request())); + } + + private Response decompress(Response response) { + ResponseBody body = response.body(); + if (body == null) { + return response; + } + String encoding = response.header("Content-Encoding"); + if (encoding == null) { + return response; + } + Source decompressedSource; + if (encoding.equalsIgnoreCase("gzip") || encoding.equalsIgnoreCase("x-gzip")) { + decompressedSource = new GzipSource(body.source()); + } else if (encoding.equalsIgnoreCase("deflate")) { + decompressedSource = new InflaterSource(body.source(), new Inflater()); + } else { + return response; + } + ResponseBody decompressedBody = ResponseBody.create(body.contentType(), -1L, Okio.buffer(decompressedSource)); + return response.newBuilder() + .headers(response.headers() + .newBuilder() + .removeAll("Content-Encoding") + .removeAll("Content-Length") + .build()) + .body(decompressedBody) + .build(); + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/core/RetryInterceptor.java b/common/src/main/java/com/skyflow/generated/detect/rest/core/RetryInterceptor.java new file mode 100644 index 00000000..c46a526e --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/core/RetryInterceptor.java @@ -0,0 +1,242 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.core; + +import java.io.IOException; +import java.time.Duration; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.Optional; +import java.util.Random; +import okhttp3.Interceptor; +import okhttp3.Request; +import okhttp3.Response; + +public class RetryInterceptor implements Interceptor { + + private static final Duration DEFAULT_INITIAL_RETRY_DELAY = Duration.ofMillis(1000); + private static final Duration DEFAULT_MAX_RETRY_DELAY = Duration.ofMillis(60000); + private static final double DEFAULT_JITTER_FACTOR = 0.2; + + private final int maxRetries; + private final Duration initialRetryDelay; + private final Duration maxRetryDelay; + private final double jitterFactor; + private final Random random = new Random(); + + public RetryInterceptor(int maxRetries) { + this(maxRetries, Optional.empty(), Optional.empty(), Optional.empty()); + } + + public RetryInterceptor( + int maxRetries, + Optional initialRetryDelayMillis, + Optional maxRetryDelayMillis, + Optional jitterFactor) { + initialRetryDelayMillis.ifPresent(delay -> { + if (delay < 0) { + throw new IllegalArgumentException("initialRetryDelayMillis must be non-negative"); + } + }); + maxRetryDelayMillis.ifPresent(delay -> { + if (delay < 0) { + throw new IllegalArgumentException("maxRetryDelayMillis must be non-negative"); + } + }); + jitterFactor.ifPresent(factor -> { + if (factor < 0 || factor > 1) { + throw new IllegalArgumentException("jitterFactor must be between 0 and 1"); + } + }); + this.maxRetries = maxRetries; + this.initialRetryDelay = initialRetryDelayMillis.map(Duration::ofMillis).orElse(DEFAULT_INITIAL_RETRY_DELAY); + this.maxRetryDelay = maxRetryDelayMillis.map(Duration::ofMillis).orElse(DEFAULT_MAX_RETRY_DELAY); + this.jitterFactor = jitterFactor.orElse(DEFAULT_JITTER_FACTOR); + } + + @Override + public Response intercept(Chain chain) throws IOException { + Request request = chain.request(); + int effectiveMaxRetries = resolveMaxRetries(request); + Response response = chain.proceed(request); + + if (shouldRetry(response.code())) { + return retryChain(response, chain, effectiveMaxRetries); + } + + return response; + } + + private int resolveMaxRetries(Request request) { + MaxRetriesOverride override = request.tag(MaxRetriesOverride.class); + return override != null ? override.getValue() : this.maxRetries; + } + + private Response retryChain(Response response, Chain chain, int maxRetries) throws IOException { + ExponentialBackoff backoff = new ExponentialBackoff(maxRetries); + Optional nextBackoff = backoff.nextBackoff(response); + while (nextBackoff.isPresent()) { + try { + Thread.sleep(nextBackoff.get().toMillis()); + } catch (InterruptedException e) { + throw new IOException("Interrupted while trying request", e); + } + response.close(); + response = chain.proceed(chain.request()); + if (shouldRetry(response.code())) { + nextBackoff = backoff.nextBackoff(response); + } else { + return response; + } + } + + return response; + } + + /** + * Calculates the retry delay from response headers, with fallback to exponential backoff. + * Priority: Retry-After > X-RateLimit-Reset > Exponential Backoff + */ + private Duration getRetryDelayFromHeaders(Response response, int retryAttempt) { + // Check for Retry-After header first (RFC 7231), with no jitter + String retryAfter = response.header("Retry-After"); + if (retryAfter != null) { + // Parse as number of seconds... + Optional secondsDelay = tryParseLong(retryAfter) + .map(seconds -> seconds * 1000) + .filter(delayMs -> delayMs > 0) + .map(delayMs -> Math.min(delayMs, maxRetryDelay.toMillis())) + .map(Duration::ofMillis); + if (secondsDelay.isPresent()) { + return secondsDelay.get(); + } + + // ...or as an HTTP date; both are valid + Optional dateDelay = tryParseHttpDate(retryAfter) + .map(resetTime -> resetTime.toInstant().toEpochMilli() - System.currentTimeMillis()) + .filter(delayMs -> delayMs > 0) + .map(delayMs -> Math.min(delayMs, maxRetryDelay.toMillis())) + .map(Duration::ofMillis); + if (dateDelay.isPresent()) { + return dateDelay.get(); + } + } + + // Then check for industry-standard X-RateLimit-Reset header, with positive jitter + String rateLimitReset = response.header("X-RateLimit-Reset"); + if (rateLimitReset != null) { + // Assume Unix timestamp in epoch seconds + Optional rateLimitDelay = tryParseLong(rateLimitReset) + .map(resetTimeSeconds -> (resetTimeSeconds * 1000) - System.currentTimeMillis()) + .filter(delayMs -> delayMs > 0) + .map(delayMs -> Math.min(delayMs, maxRetryDelay.toMillis())) + .map(this::addPositiveJitter) + .map(Duration::ofMillis); + if (rateLimitDelay.isPresent()) { + return rateLimitDelay.get(); + } + } + + // Fall back to exponential backoff, with symmetric jitter + long initialDelayMillis = initialRetryDelay.toMillis(); + long maxDelayMillis = maxRetryDelay.toMillis(); + long cappedDelay; + if (retryAttempt >= Long.SIZE - 1 || initialDelayMillis > (maxDelayMillis >> retryAttempt)) { + // initialDelayMillis * 2^retryAttempt would exceed maxDelayMillis (or overflow) + cappedDelay = maxDelayMillis; + } else { + cappedDelay = Math.min(initialDelayMillis << retryAttempt, maxDelayMillis); // 2^retryAttempt + } + return Duration.ofMillis(addSymmetricJitter(cappedDelay)); + } + + /** + * Attempts to parse a string as a long, returning empty Optional on failure. + */ + private Optional tryParseLong(String value) { + if (value == null) { + return Optional.empty(); + } + try { + return Optional.of(Long.parseLong(value)); + } catch (NumberFormatException e) { + return Optional.empty(); + } + } + + /** + * Attempts to parse a string as an HTTP date (RFC 1123), returning empty Optional on failure. + */ + private Optional tryParseHttpDate(String value) { + if (value == null) { + return Optional.empty(); + } + try { + return Optional.of(ZonedDateTime.parse(value, DateTimeFormatter.RFC_1123_DATE_TIME)); + } catch (DateTimeParseException e) { + return Optional.empty(); + } + } + + /** + * Adds positive jitter (100-120% of original value) to prevent thundering herd. + * Used for X-RateLimit-Reset header delays. + */ + private long addPositiveJitter(long delayMs) { + double jitterMultiplier = 1.0 + (random.nextDouble() * jitterFactor); + return (long) (delayMs * jitterMultiplier); + } + + /** + * Adds symmetric jitter (90-110% of original value) to prevent thundering herd. + * Used for exponential backoff delays. + */ + private long addSymmetricJitter(long delayMs) { + double jitterMultiplier = 1.0 + ((random.nextDouble() - 0.5) * jitterFactor); + return (long) (delayMs * jitterMultiplier); + } + + private static boolean shouldRetry(int statusCode) { + return statusCode == 408 || statusCode == 429 || statusCode >= 500; + } + + /** + * Per-request override carried on the OkHttp {@link Request} as a tag. + * When present, the interceptor uses this value instead of the client-wide + * {@code maxRetries} configured at construction time. + */ + public static final class MaxRetriesOverride { + private final int value; + + public MaxRetriesOverride(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + } + + private final class ExponentialBackoff { + + private final int maxNumRetries; + + private int retryNumber = 0; + + ExponentialBackoff(int maxNumRetries) { + this.maxNumRetries = maxNumRetries; + } + + public Optional nextBackoff(Response response) { + if (retryNumber >= maxNumRetries) { + return Optional.empty(); + } + + Duration delay = getRetryDelayFromHeaders(response, retryNumber); + retryNumber += 1; + return Optional.of(delay); + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/errors/BadRequestError.java b/common/src/main/java/com/skyflow/generated/detect/rest/errors/BadRequestError.java new file mode 100644 index 00000000..7858ef63 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/errors/BadRequestError.java @@ -0,0 +1,33 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.errors; + +import com.skyflow.generated.detect.rest.core.ApiClientApiException; +import com.skyflow.generated.detect.rest.types.ErrorResponse; +import okhttp3.Response; + +public final class BadRequestError extends ApiClientApiException { + /** + * The body of the response that triggered the exception. + */ + private final ErrorResponse body; + + public BadRequestError(ErrorResponse body) { + super("BadRequestError", 400, body); + this.body = body; + } + + public BadRequestError(ErrorResponse body, Response rawResponse) { + super("BadRequestError", 400, body, rawResponse); + this.body = body; + } + + /** + * @return the body + */ + @java.lang.Override + public ErrorResponse body() { + return this.body; + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/errors/InternalServerError.java b/common/src/main/java/com/skyflow/generated/detect/rest/errors/InternalServerError.java new file mode 100644 index 00000000..c21d7766 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/errors/InternalServerError.java @@ -0,0 +1,33 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.errors; + +import com.skyflow.generated.detect.rest.core.ApiClientApiException; +import com.skyflow.generated.detect.rest.types.ErrorResponse; +import okhttp3.Response; + +public final class InternalServerError extends ApiClientApiException { + /** + * The body of the response that triggered the exception. + */ + private final ErrorResponse body; + + public InternalServerError(ErrorResponse body) { + super("InternalServerError", 500, body); + this.body = body; + } + + public InternalServerError(ErrorResponse body, Response rawResponse) { + super("InternalServerError", 500, body, rawResponse); + this.body = body; + } + + /** + * @return the body + */ + @java.lang.Override + public ErrorResponse body() { + return this.body; + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/errors/UnauthorizedError.java b/common/src/main/java/com/skyflow/generated/detect/rest/errors/UnauthorizedError.java new file mode 100644 index 00000000..63754690 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/errors/UnauthorizedError.java @@ -0,0 +1,33 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.errors; + +import com.skyflow.generated.detect.rest.core.ApiClientApiException; +import com.skyflow.generated.detect.rest.types.ErrorResponse; +import okhttp3.Response; + +public final class UnauthorizedError extends ApiClientApiException { + /** + * The body of the response that triggered the exception. + */ + private final ErrorResponse body; + + public UnauthorizedError(ErrorResponse body) { + super("UnauthorizedError", 401, body); + this.body = body; + } + + public UnauthorizedError(ErrorResponse body, Response rawResponse) { + super("UnauthorizedError", 401, body, rawResponse); + this.body = body; + } + + /** + * @return the body + */ + @java.lang.Override + public ErrorResponse body() { + return this.body; + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/resources/filesv2/AsyncFilesV2Client.java b/common/src/main/java/com/skyflow/generated/detect/rest/resources/filesv2/AsyncFilesV2Client.java new file mode 100644 index 00000000..c032d144 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/resources/filesv2/AsyncFilesV2Client.java @@ -0,0 +1,85 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.resources.filesv2; + +import com.skyflow.generated.detect.rest.core.ClientOptions; +import com.skyflow.generated.detect.rest.core.RequestOptions; +import com.skyflow.generated.detect.rest.resources.filesv2.requests.DeidentifyFileRequestV2; +import com.skyflow.generated.detect.rest.resources.filesv2.requests.GetRunV2Request; +import com.skyflow.generated.detect.rest.resources.filesv2.requests.ReidentifyFileRequestV2; +import com.skyflow.generated.detect.rest.types.DeidentifyFileResponseV2; +import com.skyflow.generated.detect.rest.types.DetectRunsResponseV2; +import com.skyflow.generated.detect.rest.types.ReidentifyFileResponseV2; +import java.util.concurrent.CompletableFuture; + +public class AsyncFilesV2Client { + protected final ClientOptions clientOptions; + + private final AsyncRawFilesV2Client rawClient; + + public AsyncFilesV2Client(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + this.rawClient = new AsyncRawFilesV2Client(clientOptions); + } + + /** + * Get responses with HTTP metadata like headers + */ + public AsyncRawFilesV2Client withRawResponse() { + return this.rawClient; + } + + /** + * De-identifies sensitive data from a file.

Note: + *

    + *
  • Layout may vary with token-based redaction for MS Office files (PPT, DOCX, and XLS) and PDFs when token-based PDF processing is selected.
  • + *
  • V2 API is in beta.
  • + *
+ */ + public CompletableFuture deidentifyFileV2(DeidentifyFileRequestV2 request) { + return this.rawClient.deidentifyFileV2(request).thenApply(response -> response.body()); + } + + /** + * De-identifies sensitive data from a file.

Note: + *
    + *
  • Layout may vary with token-based redaction for MS Office files (PPT, DOCX, and XLS) and PDFs when token-based PDF processing is selected.
  • + *
  • V2 API is in beta.
  • + *
+ */ + public CompletableFuture deidentifyFileV2( + DeidentifyFileRequestV2 request, RequestOptions requestOptions) { + return this.rawClient.deidentifyFileV2(request, requestOptions).thenApply(response -> response.body()); + } + + /** + * Re-identifies tokens in a file.

Requires the Detect Invoker, Detect Manager, or Vault Owner role. Re-identifying vault_token values also requires a policy that separately grants the Detokenization action, unless the caller has the Vault Owner role.

Note: V2 API is in beta. + */ + public CompletableFuture reidentifyFileV2(ReidentifyFileRequestV2 request) { + return this.rawClient.reidentifyFileV2(request).thenApply(response -> response.body()); + } + + /** + * Re-identifies tokens in a file.

Requires the Detect Invoker, Detect Manager, or Vault Owner role. Re-identifying vault_token values also requires a policy that separately grants the Detokenization action, unless the caller has the Vault Owner role.

Note: V2 API is in beta. + */ + public CompletableFuture reidentifyFileV2( + ReidentifyFileRequestV2 request, RequestOptions requestOptions) { + return this.rawClient.reidentifyFileV2(request, requestOptions).thenApply(response -> response.body()); + } + + /** + * Returns the status of a detect run.

Note: V2 API is in beta. + */ + public CompletableFuture getRunV2(String runId, GetRunV2Request request) { + return this.rawClient.getRunV2(runId, request).thenApply(response -> response.body()); + } + + /** + * Returns the status of a detect run.

Note: V2 API is in beta. + */ + public CompletableFuture getRunV2( + String runId, GetRunV2Request request, RequestOptions requestOptions) { + return this.rawClient.getRunV2(runId, request, requestOptions).thenApply(response -> response.body()); + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/resources/filesv2/AsyncRawFilesV2Client.java b/common/src/main/java/com/skyflow/generated/detect/rest/resources/filesv2/AsyncRawFilesV2Client.java new file mode 100644 index 00000000..6764e32b --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/resources/filesv2/AsyncRawFilesV2Client.java @@ -0,0 +1,350 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.resources.filesv2; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.skyflow.generated.detect.rest.core.ApiClientApiException; +import com.skyflow.generated.detect.rest.core.ApiClientException; +import com.skyflow.generated.detect.rest.core.ApiClientHttpResponse; +import com.skyflow.generated.detect.rest.core.ClientOptions; +import com.skyflow.generated.detect.rest.core.MediaTypes; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import com.skyflow.generated.detect.rest.core.QueryStringMapper; +import com.skyflow.generated.detect.rest.core.RequestOptions; +import com.skyflow.generated.detect.rest.core.RetryInterceptor; +import com.skyflow.generated.detect.rest.errors.BadRequestError; +import com.skyflow.generated.detect.rest.errors.InternalServerError; +import com.skyflow.generated.detect.rest.errors.UnauthorizedError; +import com.skyflow.generated.detect.rest.resources.filesv2.requests.DeidentifyFileRequestV2; +import com.skyflow.generated.detect.rest.resources.filesv2.requests.GetRunV2Request; +import com.skyflow.generated.detect.rest.resources.filesv2.requests.ReidentifyFileRequestV2; +import com.skyflow.generated.detect.rest.types.DeidentifyFileResponseV2; +import com.skyflow.generated.detect.rest.types.DetectRunsResponseV2; +import com.skyflow.generated.detect.rest.types.ErrorResponse; +import com.skyflow.generated.detect.rest.types.ReidentifyFileResponseV2; +import java.io.IOException; +import java.util.concurrent.CompletableFuture; +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.Headers; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.ResponseBody; +import org.jetbrains.annotations.NotNull; + +public class AsyncRawFilesV2Client { + protected final ClientOptions clientOptions; + + public AsyncRawFilesV2Client(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + } + + /** + * De-identifies sensitive data from a file.

Note: + *
    + *
  • Layout may vary with token-based redaction for MS Office files (PPT, DOCX, and XLS) and PDFs when token-based PDF processing is selected.
  • + *
  • V2 API is in beta.
  • + *
+ */ + public CompletableFuture> deidentifyFileV2( + DeidentifyFileRequestV2 request) { + return deidentifyFileV2(request, null); + } + + /** + * De-identifies sensitive data from a file.

Note: + *
    + *
  • Layout may vary with token-based redaction for MS Office files (PPT, DOCX, and XLS) and PDFs when token-based PDF processing is selected.
  • + *
  • V2 API is in beta.
  • + *
+ */ + public CompletableFuture> deidentifyFileV2( + DeidentifyFileRequestV2 request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("v2/detect/deidentify/file"); + if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + }); + } + RequestBody body; + try { + body = RequestBody.create( + ObjectMappers.JSON_MAPPER.writeValueAsBytes(request), MediaTypes.APPLICATION_JSON); + } catch (JsonProcessingException e) { + throw new ApiClientException("Failed to serialize request", e); + } + Request okhttpRequest = new Request.Builder() + .url(httpUrl.build()) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .addHeader("Content-Type", "application/json") + .addHeader("Accept", "application/json") + .build(); + OkHttpClient client = clientOptions.httpClient(); + if (requestOptions != null && requestOptions.getTimeout().isPresent()) { + client = clientOptions.httpClientWithTimeout(requestOptions); + } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } + CompletableFuture> future = new CompletableFuture<>(); + client.newCall(okhttpRequest).enqueue(new Callback() { + @Override + public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { + try (ResponseBody responseBody = response.body()) { + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + future.complete(new ApiClientHttpResponse<>( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, DeidentifyFileResponseV2.class), + response)); + return; + } + try { + switch (response.code()) { + case 400: + future.completeExceptionally(new BadRequestError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), + response)); + return; + case 401: + future.completeExceptionally(new UnauthorizedError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), + response)); + return; + case 500: + future.completeExceptionally(new InternalServerError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), + response)); + return; + } + } catch (JsonProcessingException ignored) { + // unable to map error response, throwing generic error + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + future.completeExceptionally(new ApiClientApiException( + "Error with status code " + response.code(), response.code(), errorBody, response)); + return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new ApiClientException("Failed to deserialize response: " + e.getMessage(), e)); + } catch (IOException e) { + future.completeExceptionally(new ApiClientException("Network error executing HTTP request", e)); + } + } + + @Override + public void onFailure(@NotNull Call call, @NotNull IOException e) { + future.completeExceptionally(new ApiClientException("Network error executing HTTP request", e)); + } + }); + return future; + } + + /** + * Re-identifies tokens in a file.

Requires the Detect Invoker, Detect Manager, or Vault Owner role. Re-identifying vault_token values also requires a policy that separately grants the Detokenization action, unless the caller has the Vault Owner role.

Note: V2 API is in beta. + */ + public CompletableFuture> reidentifyFileV2( + ReidentifyFileRequestV2 request) { + return reidentifyFileV2(request, null); + } + + /** + * Re-identifies tokens in a file.

Requires the Detect Invoker, Detect Manager, or Vault Owner role. Re-identifying vault_token values also requires a policy that separately grants the Detokenization action, unless the caller has the Vault Owner role.

Note: V2 API is in beta. + */ + public CompletableFuture> reidentifyFileV2( + ReidentifyFileRequestV2 request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("v2/detect/reidentify/file"); + if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + }); + } + RequestBody body; + try { + body = RequestBody.create( + ObjectMappers.JSON_MAPPER.writeValueAsBytes(request), MediaTypes.APPLICATION_JSON); + } catch (JsonProcessingException e) { + throw new ApiClientException("Failed to serialize request", e); + } + Request okhttpRequest = new Request.Builder() + .url(httpUrl.build()) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .addHeader("Content-Type", "application/json") + .addHeader("Accept", "application/json") + .build(); + OkHttpClient client = clientOptions.httpClient(); + if (requestOptions != null && requestOptions.getTimeout().isPresent()) { + client = clientOptions.httpClientWithTimeout(requestOptions); + } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } + CompletableFuture> future = new CompletableFuture<>(); + client.newCall(okhttpRequest).enqueue(new Callback() { + @Override + public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { + try (ResponseBody responseBody = response.body()) { + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + future.complete(new ApiClientHttpResponse<>( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ReidentifyFileResponseV2.class), + response)); + return; + } + try { + switch (response.code()) { + case 400: + future.completeExceptionally(new BadRequestError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), + response)); + return; + case 401: + future.completeExceptionally(new UnauthorizedError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), + response)); + return; + case 500: + future.completeExceptionally(new InternalServerError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), + response)); + return; + } + } catch (JsonProcessingException ignored) { + // unable to map error response, throwing generic error + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + future.completeExceptionally(new ApiClientApiException( + "Error with status code " + response.code(), response.code(), errorBody, response)); + return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new ApiClientException("Failed to deserialize response: " + e.getMessage(), e)); + } catch (IOException e) { + future.completeExceptionally(new ApiClientException("Network error executing HTTP request", e)); + } + } + + @Override + public void onFailure(@NotNull Call call, @NotNull IOException e) { + future.completeExceptionally(new ApiClientException("Network error executing HTTP request", e)); + } + }); + return future; + } + + /** + * Returns the status of a detect run.

Note: V2 API is in beta. + */ + public CompletableFuture> getRunV2( + String runId, GetRunV2Request request) { + return getRunV2(runId, request, null); + } + + /** + * Returns the status of a detect run.

Note: V2 API is in beta. + */ + public CompletableFuture> getRunV2( + String runId, GetRunV2Request request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("v2/detect/runs") + .addPathSegment(runId); + QueryStringMapper.addQueryParameter(httpUrl, "vaultId", request.getVaultId(), false); + if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + }); + } + Request.Builder _requestBuilder = new Request.Builder() + .url(httpUrl.build()) + .method("GET", null) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .addHeader("Accept", "application/json"); + Request okhttpRequest = _requestBuilder.build(); + OkHttpClient client = clientOptions.httpClient(); + if (requestOptions != null && requestOptions.getTimeout().isPresent()) { + client = clientOptions.httpClientWithTimeout(requestOptions); + } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } + CompletableFuture> future = new CompletableFuture<>(); + client.newCall(okhttpRequest).enqueue(new Callback() { + @Override + public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { + try (ResponseBody responseBody = response.body()) { + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + future.complete(new ApiClientHttpResponse<>( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, DetectRunsResponseV2.class), + response)); + return; + } + try { + switch (response.code()) { + case 400: + future.completeExceptionally(new BadRequestError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), + response)); + return; + case 401: + future.completeExceptionally(new UnauthorizedError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), + response)); + return; + case 500: + future.completeExceptionally(new InternalServerError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), + response)); + return; + } + } catch (JsonProcessingException ignored) { + // unable to map error response, throwing generic error + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + future.completeExceptionally(new ApiClientApiException( + "Error with status code " + response.code(), response.code(), errorBody, response)); + return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new ApiClientException("Failed to deserialize response: " + e.getMessage(), e)); + } catch (IOException e) { + future.completeExceptionally(new ApiClientException("Network error executing HTTP request", e)); + } + } + + @Override + public void onFailure(@NotNull Call call, @NotNull IOException e) { + future.completeExceptionally(new ApiClientException("Network error executing HTTP request", e)); + } + }); + return future; + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/resources/filesv2/FilesV2Client.java b/common/src/main/java/com/skyflow/generated/detect/rest/resources/filesv2/FilesV2Client.java new file mode 100644 index 00000000..9f9f8c1a --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/resources/filesv2/FilesV2Client.java @@ -0,0 +1,81 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.resources.filesv2; + +import com.skyflow.generated.detect.rest.core.ClientOptions; +import com.skyflow.generated.detect.rest.core.RequestOptions; +import com.skyflow.generated.detect.rest.resources.filesv2.requests.DeidentifyFileRequestV2; +import com.skyflow.generated.detect.rest.resources.filesv2.requests.GetRunV2Request; +import com.skyflow.generated.detect.rest.resources.filesv2.requests.ReidentifyFileRequestV2; +import com.skyflow.generated.detect.rest.types.DeidentifyFileResponseV2; +import com.skyflow.generated.detect.rest.types.DetectRunsResponseV2; +import com.skyflow.generated.detect.rest.types.ReidentifyFileResponseV2; + +public class FilesV2Client { + protected final ClientOptions clientOptions; + + private final RawFilesV2Client rawClient; + + public FilesV2Client(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + this.rawClient = new RawFilesV2Client(clientOptions); + } + + /** + * Get responses with HTTP metadata like headers + */ + public RawFilesV2Client withRawResponse() { + return this.rawClient; + } + + /** + * De-identifies sensitive data from a file.

Note: + *
    + *
  • Layout may vary with token-based redaction for MS Office files (PPT, DOCX, and XLS) and PDFs when token-based PDF processing is selected.
  • + *
  • V2 API is in beta.
  • + *
+ */ + public DeidentifyFileResponseV2 deidentifyFileV2(DeidentifyFileRequestV2 request) { + return this.rawClient.deidentifyFileV2(request).body(); + } + + /** + * De-identifies sensitive data from a file.

Note: + *
    + *
  • Layout may vary with token-based redaction for MS Office files (PPT, DOCX, and XLS) and PDFs when token-based PDF processing is selected.
  • + *
  • V2 API is in beta.
  • + *
+ */ + public DeidentifyFileResponseV2 deidentifyFileV2(DeidentifyFileRequestV2 request, RequestOptions requestOptions) { + return this.rawClient.deidentifyFileV2(request, requestOptions).body(); + } + + /** + * Re-identifies tokens in a file.

Requires the Detect Invoker, Detect Manager, or Vault Owner role. Re-identifying vault_token values also requires a policy that separately grants the Detokenization action, unless the caller has the Vault Owner role.

Note: V2 API is in beta. + */ + public ReidentifyFileResponseV2 reidentifyFileV2(ReidentifyFileRequestV2 request) { + return this.rawClient.reidentifyFileV2(request).body(); + } + + /** + * Re-identifies tokens in a file.

Requires the Detect Invoker, Detect Manager, or Vault Owner role. Re-identifying vault_token values also requires a policy that separately grants the Detokenization action, unless the caller has the Vault Owner role.

Note: V2 API is in beta. + */ + public ReidentifyFileResponseV2 reidentifyFileV2(ReidentifyFileRequestV2 request, RequestOptions requestOptions) { + return this.rawClient.reidentifyFileV2(request, requestOptions).body(); + } + + /** + * Returns the status of a detect run.

Note: V2 API is in beta. + */ + public DetectRunsResponseV2 getRunV2(String runId, GetRunV2Request request) { + return this.rawClient.getRunV2(runId, request).body(); + } + + /** + * Returns the status of a detect run.

Note: V2 API is in beta. + */ + public DetectRunsResponseV2 getRunV2(String runId, GetRunV2Request request, RequestOptions requestOptions) { + return this.rawClient.getRunV2(runId, request, requestOptions).body(); + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/resources/filesv2/RawFilesV2Client.java b/common/src/main/java/com/skyflow/generated/detect/rest/resources/filesv2/RawFilesV2Client.java new file mode 100644 index 00000000..2ffe352d --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/resources/filesv2/RawFilesV2Client.java @@ -0,0 +1,282 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.resources.filesv2; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.skyflow.generated.detect.rest.core.ApiClientApiException; +import com.skyflow.generated.detect.rest.core.ApiClientException; +import com.skyflow.generated.detect.rest.core.ApiClientHttpResponse; +import com.skyflow.generated.detect.rest.core.ClientOptions; +import com.skyflow.generated.detect.rest.core.MediaTypes; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import com.skyflow.generated.detect.rest.core.QueryStringMapper; +import com.skyflow.generated.detect.rest.core.RequestOptions; +import com.skyflow.generated.detect.rest.core.RetryInterceptor; +import com.skyflow.generated.detect.rest.errors.BadRequestError; +import com.skyflow.generated.detect.rest.errors.InternalServerError; +import com.skyflow.generated.detect.rest.errors.UnauthorizedError; +import com.skyflow.generated.detect.rest.resources.filesv2.requests.DeidentifyFileRequestV2; +import com.skyflow.generated.detect.rest.resources.filesv2.requests.GetRunV2Request; +import com.skyflow.generated.detect.rest.resources.filesv2.requests.ReidentifyFileRequestV2; +import com.skyflow.generated.detect.rest.types.DeidentifyFileResponseV2; +import com.skyflow.generated.detect.rest.types.DetectRunsResponseV2; +import com.skyflow.generated.detect.rest.types.ErrorResponse; +import com.skyflow.generated.detect.rest.types.ReidentifyFileResponseV2; +import java.io.IOException; +import okhttp3.Headers; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.ResponseBody; + +public class RawFilesV2Client { + protected final ClientOptions clientOptions; + + public RawFilesV2Client(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + } + + /** + * De-identifies sensitive data from a file.

Note: + *
    + *
  • Layout may vary with token-based redaction for MS Office files (PPT, DOCX, and XLS) and PDFs when token-based PDF processing is selected.
  • + *
  • V2 API is in beta.
  • + *
+ */ + public ApiClientHttpResponse deidentifyFileV2(DeidentifyFileRequestV2 request) { + return deidentifyFileV2(request, null); + } + + /** + * De-identifies sensitive data from a file.

Note: + *
    + *
  • Layout may vary with token-based redaction for MS Office files (PPT, DOCX, and XLS) and PDFs when token-based PDF processing is selected.
  • + *
  • V2 API is in beta.
  • + *
+ */ + public ApiClientHttpResponse deidentifyFileV2( + DeidentifyFileRequestV2 request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("v2/detect/deidentify/file"); + if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + }); + } + RequestBody body; + try { + body = RequestBody.create( + ObjectMappers.JSON_MAPPER.writeValueAsBytes(request), MediaTypes.APPLICATION_JSON); + } catch (JsonProcessingException e) { + throw new ApiClientException("Failed to serialize request", e); + } + Request okhttpRequest = new Request.Builder() + .url(httpUrl.build()) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .addHeader("Content-Type", "application/json") + .addHeader("Accept", "application/json") + .build(); + OkHttpClient client = clientOptions.httpClient(); + if (requestOptions != null && requestOptions.getTimeout().isPresent()) { + client = clientOptions.httpClientWithTimeout(requestOptions); + } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } + try (Response response = client.newCall(okhttpRequest).execute()) { + ResponseBody responseBody = response.body(); + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + return new ApiClientHttpResponse<>( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, DeidentifyFileResponseV2.class), + response); + } + try { + switch (response.code()) { + case 400: + throw new BadRequestError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), response); + case 401: + throw new UnauthorizedError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), response); + case 500: + throw new InternalServerError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), response); + } + } catch (JsonProcessingException ignored) { + // unable to map error response, throwing generic error + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + throw new ApiClientApiException( + "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new ApiClientException("Failed to deserialize response: " + e.getMessage(), e); + } catch (IOException e) { + throw new ApiClientException("Network error executing HTTP request", e); + } + } + + /** + * Re-identifies tokens in a file.

Requires the Detect Invoker, Detect Manager, or Vault Owner role. Re-identifying vault_token values also requires a policy that separately grants the Detokenization action, unless the caller has the Vault Owner role.

Note: V2 API is in beta. + */ + public ApiClientHttpResponse reidentifyFileV2(ReidentifyFileRequestV2 request) { + return reidentifyFileV2(request, null); + } + + /** + * Re-identifies tokens in a file.

Requires the Detect Invoker, Detect Manager, or Vault Owner role. Re-identifying vault_token values also requires a policy that separately grants the Detokenization action, unless the caller has the Vault Owner role.

Note: V2 API is in beta. + */ + public ApiClientHttpResponse reidentifyFileV2( + ReidentifyFileRequestV2 request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("v2/detect/reidentify/file"); + if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + }); + } + RequestBody body; + try { + body = RequestBody.create( + ObjectMappers.JSON_MAPPER.writeValueAsBytes(request), MediaTypes.APPLICATION_JSON); + } catch (JsonProcessingException e) { + throw new ApiClientException("Failed to serialize request", e); + } + Request okhttpRequest = new Request.Builder() + .url(httpUrl.build()) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .addHeader("Content-Type", "application/json") + .addHeader("Accept", "application/json") + .build(); + OkHttpClient client = clientOptions.httpClient(); + if (requestOptions != null && requestOptions.getTimeout().isPresent()) { + client = clientOptions.httpClientWithTimeout(requestOptions); + } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } + try (Response response = client.newCall(okhttpRequest).execute()) { + ResponseBody responseBody = response.body(); + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + return new ApiClientHttpResponse<>( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ReidentifyFileResponseV2.class), + response); + } + try { + switch (response.code()) { + case 400: + throw new BadRequestError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), response); + case 401: + throw new UnauthorizedError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), response); + case 500: + throw new InternalServerError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), response); + } + } catch (JsonProcessingException ignored) { + // unable to map error response, throwing generic error + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + throw new ApiClientApiException( + "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new ApiClientException("Failed to deserialize response: " + e.getMessage(), e); + } catch (IOException e) { + throw new ApiClientException("Network error executing HTTP request", e); + } + } + + /** + * Returns the status of a detect run.

Note: V2 API is in beta. + */ + public ApiClientHttpResponse getRunV2(String runId, GetRunV2Request request) { + return getRunV2(runId, request, null); + } + + /** + * Returns the status of a detect run.

Note: V2 API is in beta. + */ + public ApiClientHttpResponse getRunV2( + String runId, GetRunV2Request request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("v2/detect/runs") + .addPathSegment(runId); + QueryStringMapper.addQueryParameter(httpUrl, "vaultId", request.getVaultId(), false); + if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + }); + } + Request.Builder _requestBuilder = new Request.Builder() + .url(httpUrl.build()) + .method("GET", null) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .addHeader("Accept", "application/json"); + Request okhttpRequest = _requestBuilder.build(); + OkHttpClient client = clientOptions.httpClient(); + if (requestOptions != null && requestOptions.getTimeout().isPresent()) { + client = clientOptions.httpClientWithTimeout(requestOptions); + } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } + try (Response response = client.newCall(okhttpRequest).execute()) { + ResponseBody responseBody = response.body(); + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + return new ApiClientHttpResponse<>( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, DetectRunsResponseV2.class), response); + } + try { + switch (response.code()) { + case 400: + throw new BadRequestError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), response); + case 401: + throw new UnauthorizedError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), response); + case 500: + throw new InternalServerError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), response); + } + } catch (JsonProcessingException ignored) { + // unable to map error response, throwing generic error + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + throw new ApiClientApiException( + "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new ApiClientException("Failed to deserialize response: " + e.getMessage(), e); + } catch (IOException e) { + throw new ApiClientException("Network error executing HTTP request", e); + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/resources/filesv2/requests/DeidentifyFileRequestV2.java b/common/src/main/java/com/skyflow/generated/detect/rest/resources/filesv2/requests/DeidentifyFileRequestV2.java new file mode 100644 index 00000000..f95ee7ab --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/resources/filesv2/requests/DeidentifyFileRequestV2.java @@ -0,0 +1,286 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.resources.filesv2.requests; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import com.skyflow.generated.detect.rest.resources.filesv2.types.DeidentifyFileRequestV2DataFormat; +import com.skyflow.generated.detect.rest.resources.filesv2.types.DeidentifyFileRequestV2DataSource; +import com.skyflow.generated.detect.rest.types.DetectConfigV2; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import org.jetbrains.annotations.NotNull; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = DeidentifyFileRequestV2.Builder.class) +public final class DeidentifyFileRequestV2 { + private final DeidentifyFileRequestV2DataSource dataSource; + + private final String value; + + private final Optional dataFormat; + + private final Optional configurationId; + + private final Optional configuration; + + private final Map additionalProperties; + + private DeidentifyFileRequestV2( + DeidentifyFileRequestV2DataSource dataSource, + String value, + Optional dataFormat, + Optional configurationId, + Optional configuration, + Map additionalProperties) { + this.dataSource = dataSource; + this.value = value; + this.dataFormat = dataFormat; + this.configurationId = configurationId; + this.configuration = configuration; + this.additionalProperties = additionalProperties; + } + + /** + * @return Data source of the input file. BASE64: Base64-encoded file string. SKYFLOW_ID: Reference file by vault ID. PRESIGNED_URL: S3 presigned URL of input file. + */ + @JsonProperty("dataSource") + public DeidentifyFileRequestV2DataSource getDataSource() { + return dataSource; + } + + /** + * @return File data corresponding to the specified dataSource type. + */ + @JsonProperty("value") + public String getValue() { + return value; + } + + /** + * @return Format of the input file. The VAULT_TOKEN de-identification type is supported only for txt, json, csv, xml, jsonl, docx, xlsx, and pptx files. + */ + @JsonProperty("dataFormat") + public Optional getDataFormat() { + return dataFormat; + } + + /** + * @return ID of the Detect configuration to use for de-identification. + */ + @JsonProperty("configurationId") + public Optional getConfigurationId() { + return configurationId; + } + + @JsonProperty("configuration") + public Optional getConfiguration() { + return configuration; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof DeidentifyFileRequestV2 && equalTo((DeidentifyFileRequestV2) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(DeidentifyFileRequestV2 other) { + return dataSource.equals(other.dataSource) + && value.equals(other.value) + && dataFormat.equals(other.dataFormat) + && configurationId.equals(other.configurationId) + && configuration.equals(other.configuration); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.dataSource, this.value, this.dataFormat, this.configurationId, this.configuration); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static DataSourceStage builder() { + return new Builder(); + } + + public interface DataSourceStage { + /** + *

Data source of the input file. BASE64: Base64-encoded file string. SKYFLOW_ID: Reference file by vault ID. PRESIGNED_URL: S3 presigned URL of input file.

+ */ + ValueStage dataSource(@NotNull DeidentifyFileRequestV2DataSource dataSource); + + Builder from(DeidentifyFileRequestV2 other); + } + + public interface ValueStage { + /** + *

File data corresponding to the specified dataSource type.

+ */ + _FinalStage value(@NotNull String value); + } + + public interface _FinalStage { + DeidentifyFileRequestV2 build(); + + _FinalStage additionalProperty(String key, Object value); + + _FinalStage additionalProperties(Map additionalProperties); + + /** + *

Format of the input file. The VAULT_TOKEN de-identification type is supported only for txt, json, csv, xml, jsonl, docx, xlsx, and pptx files.

+ */ + _FinalStage dataFormat(Optional dataFormat); + + _FinalStage dataFormat(DeidentifyFileRequestV2DataFormat dataFormat); + + /** + *

ID of the Detect configuration to use for de-identification.

+ */ + _FinalStage configurationId(Optional configurationId); + + _FinalStage configurationId(String configurationId); + + _FinalStage configuration(Optional configuration); + + _FinalStage configuration(DetectConfigV2 configuration); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder implements DataSourceStage, ValueStage, _FinalStage { + private DeidentifyFileRequestV2DataSource dataSource; + + private String value; + + private Optional configuration = Optional.empty(); + + private Optional configurationId = Optional.empty(); + + private Optional dataFormat = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + @java.lang.Override + public Builder from(DeidentifyFileRequestV2 other) { + dataSource(other.getDataSource()); + value(other.getValue()); + dataFormat(other.getDataFormat()); + configurationId(other.getConfigurationId()); + configuration(other.getConfiguration()); + return this; + } + + /** + *

Data source of the input file. BASE64: Base64-encoded file string. SKYFLOW_ID: Reference file by vault ID. PRESIGNED_URL: S3 presigned URL of input file.

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonSetter("dataSource") + public ValueStage dataSource(@NotNull DeidentifyFileRequestV2DataSource dataSource) { + this.dataSource = Objects.requireNonNull(dataSource, "dataSource must not be null"); + return this; + } + + /** + *

File data corresponding to the specified dataSource type.

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonSetter("value") + public _FinalStage value(@NotNull String value) { + this.value = Objects.requireNonNull(value, "value must not be null"); + return this; + } + + @java.lang.Override + public _FinalStage configuration(DetectConfigV2 configuration) { + this.configuration = Optional.ofNullable(configuration); + return this; + } + + @java.lang.Override + @JsonSetter(value = "configuration", nulls = Nulls.SKIP) + public _FinalStage configuration(Optional configuration) { + this.configuration = configuration; + return this; + } + + /** + *

ID of the Detect configuration to use for de-identification.

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + public _FinalStage configurationId(String configurationId) { + this.configurationId = Optional.ofNullable(configurationId); + return this; + } + + /** + *

ID of the Detect configuration to use for de-identification.

+ */ + @java.lang.Override + @JsonSetter(value = "configurationId", nulls = Nulls.SKIP) + public _FinalStage configurationId(Optional configurationId) { + this.configurationId = configurationId; + return this; + } + + /** + *

Format of the input file. The VAULT_TOKEN de-identification type is supported only for txt, json, csv, xml, jsonl, docx, xlsx, and pptx files.

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + public _FinalStage dataFormat(DeidentifyFileRequestV2DataFormat dataFormat) { + this.dataFormat = Optional.ofNullable(dataFormat); + return this; + } + + /** + *

Format of the input file. The VAULT_TOKEN de-identification type is supported only for txt, json, csv, xml, jsonl, docx, xlsx, and pptx files.

+ */ + @java.lang.Override + @JsonSetter(value = "dataFormat", nulls = Nulls.SKIP) + public _FinalStage dataFormat(Optional dataFormat) { + this.dataFormat = dataFormat; + return this; + } + + @java.lang.Override + public DeidentifyFileRequestV2 build() { + return new DeidentifyFileRequestV2( + dataSource, value, dataFormat, configurationId, configuration, additionalProperties); + } + + @java.lang.Override + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + @java.lang.Override + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/resources/filesv2/requests/GetRunV2Request.java b/common/src/main/java/com/skyflow/generated/detect/rest/resources/filesv2/requests/GetRunV2Request.java new file mode 100644 index 00000000..f90f7153 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/resources/filesv2/requests/GetRunV2Request.java @@ -0,0 +1,118 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.resources.filesv2.requests; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import org.jetbrains.annotations.NotNull; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = GetRunV2Request.Builder.class) +public final class GetRunV2Request { + private final String vaultId; + + private final Map additionalProperties; + + private GetRunV2Request(String vaultId, Map additionalProperties) { + this.vaultId = vaultId; + this.additionalProperties = additionalProperties; + } + + @JsonProperty("vaultId") + public String getVaultId() { + return vaultId; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof GetRunV2Request && equalTo((GetRunV2Request) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(GetRunV2Request other) { + return vaultId.equals(other.vaultId); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.vaultId); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static VaultIdStage builder() { + return new Builder(); + } + + public interface VaultIdStage { + _FinalStage vaultId(@NotNull String vaultId); + + Builder from(GetRunV2Request other); + } + + public interface _FinalStage { + GetRunV2Request build(); + + _FinalStage additionalProperty(String key, Object value); + + _FinalStage additionalProperties(Map additionalProperties); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder implements VaultIdStage, _FinalStage { + private String vaultId; + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + @java.lang.Override + public Builder from(GetRunV2Request other) { + vaultId(other.getVaultId()); + return this; + } + + @java.lang.Override + @JsonSetter("vaultId") + public _FinalStage vaultId(@NotNull String vaultId) { + this.vaultId = Objects.requireNonNull(vaultId, "vaultId must not be null"); + return this; + } + + @java.lang.Override + public GetRunV2Request build() { + return new GetRunV2Request(vaultId, additionalProperties); + } + + @java.lang.Override + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + @java.lang.Override + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/resources/filesv2/requests/ReidentifyFileRequestV2.java b/common/src/main/java/com/skyflow/generated/detect/rest/resources/filesv2/requests/ReidentifyFileRequestV2.java new file mode 100644 index 00000000..29751862 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/resources/filesv2/requests/ReidentifyFileRequestV2.java @@ -0,0 +1,291 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.resources.filesv2.requests; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import com.skyflow.generated.detect.rest.resources.filesv2.types.ReidentifyFileRequestV2DataFormat; +import com.skyflow.generated.detect.rest.resources.filesv2.types.ReidentifyFileRequestV2DataSource; +import com.skyflow.generated.detect.rest.types.RedactionLevel; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import org.jetbrains.annotations.NotNull; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = ReidentifyFileRequestV2.Builder.class) +public final class ReidentifyFileRequestV2 { + private final ReidentifyFileRequestV2DataSource dataSource; + + private final String value; + + private final Optional dataFormat; + + private final String vaultId; + + private final Optional> redactionLevel; + + private final Map additionalProperties; + + private ReidentifyFileRequestV2( + ReidentifyFileRequestV2DataSource dataSource, + String value, + Optional dataFormat, + String vaultId, + Optional> redactionLevel, + Map additionalProperties) { + this.dataSource = dataSource; + this.value = value; + this.dataFormat = dataFormat; + this.vaultId = vaultId; + this.redactionLevel = redactionLevel; + this.additionalProperties = additionalProperties; + } + + /** + * @return Data source of the input file. BASE64: Base64-encoded file string. SKYFLOW_ID: Reference file by vault ID. PRESIGNED_URL: S3 presigned URL of input file. + */ + @JsonProperty("dataSource") + public ReidentifyFileRequestV2DataSource getDataSource() { + return dataSource; + } + + /** + * @return File data corresponding to the specified dataSource type. + */ + @JsonProperty("value") + public String getValue() { + return value; + } + + /** + * @return Format of the input file. Limited to the formats that support the VAULT_TOKEN de-identification type. + */ + @JsonProperty("dataFormat") + public Optional getDataFormat() { + return dataFormat; + } + + /** + * @return ID of the vault used for de-identification. + */ + @JsonProperty("vaultId") + public String getVaultId() { + return vaultId; + } + + /** + * @return Replacement patterns or types applied to entity types or token groups during re-identification. Specifies a replacement pattern/type and the token group or entity type it applies to during re-identification. Set tokenGroupName for FlowVault and entityName for SkyVault. Similarly, set redactionType for FlowVault and redactionPattern for SkyVaultto specify the replacement. + */ + @JsonProperty("redactionLevel") + public Optional> getRedactionLevel() { + return redactionLevel; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof ReidentifyFileRequestV2 && equalTo((ReidentifyFileRequestV2) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(ReidentifyFileRequestV2 other) { + return dataSource.equals(other.dataSource) + && value.equals(other.value) + && dataFormat.equals(other.dataFormat) + && vaultId.equals(other.vaultId) + && redactionLevel.equals(other.redactionLevel); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.dataSource, this.value, this.dataFormat, this.vaultId, this.redactionLevel); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static DataSourceStage builder() { + return new Builder(); + } + + public interface DataSourceStage { + /** + *

Data source of the input file. BASE64: Base64-encoded file string. SKYFLOW_ID: Reference file by vault ID. PRESIGNED_URL: S3 presigned URL of input file.

+ */ + ValueStage dataSource(@NotNull ReidentifyFileRequestV2DataSource dataSource); + + Builder from(ReidentifyFileRequestV2 other); + } + + public interface ValueStage { + /** + *

File data corresponding to the specified dataSource type.

+ */ + VaultIdStage value(@NotNull String value); + } + + public interface VaultIdStage { + /** + *

ID of the vault used for de-identification.

+ */ + _FinalStage vaultId(@NotNull String vaultId); + } + + public interface _FinalStage { + ReidentifyFileRequestV2 build(); + + _FinalStage additionalProperty(String key, Object value); + + _FinalStage additionalProperties(Map additionalProperties); + + /** + *

Format of the input file. Limited to the formats that support the VAULT_TOKEN de-identification type.

+ */ + _FinalStage dataFormat(Optional dataFormat); + + _FinalStage dataFormat(ReidentifyFileRequestV2DataFormat dataFormat); + + /** + *

Replacement patterns or types applied to entity types or token groups during re-identification. Specifies a replacement pattern/type and the token group or entity type it applies to during re-identification. Set tokenGroupName for FlowVault and entityName for SkyVault. Similarly, set redactionType for FlowVault and redactionPattern for SkyVaultto specify the replacement.

+ */ + _FinalStage redactionLevel(Optional> redactionLevel); + + _FinalStage redactionLevel(List redactionLevel); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder implements DataSourceStage, ValueStage, VaultIdStage, _FinalStage { + private ReidentifyFileRequestV2DataSource dataSource; + + private String value; + + private String vaultId; + + private Optional> redactionLevel = Optional.empty(); + + private Optional dataFormat = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + @java.lang.Override + public Builder from(ReidentifyFileRequestV2 other) { + dataSource(other.getDataSource()); + value(other.getValue()); + dataFormat(other.getDataFormat()); + vaultId(other.getVaultId()); + redactionLevel(other.getRedactionLevel()); + return this; + } + + /** + *

Data source of the input file. BASE64: Base64-encoded file string. SKYFLOW_ID: Reference file by vault ID. PRESIGNED_URL: S3 presigned URL of input file.

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonSetter("dataSource") + public ValueStage dataSource(@NotNull ReidentifyFileRequestV2DataSource dataSource) { + this.dataSource = Objects.requireNonNull(dataSource, "dataSource must not be null"); + return this; + } + + /** + *

File data corresponding to the specified dataSource type.

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonSetter("value") + public VaultIdStage value(@NotNull String value) { + this.value = Objects.requireNonNull(value, "value must not be null"); + return this; + } + + /** + *

ID of the vault used for de-identification.

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonSetter("vaultId") + public _FinalStage vaultId(@NotNull String vaultId) { + this.vaultId = Objects.requireNonNull(vaultId, "vaultId must not be null"); + return this; + } + + /** + *

Replacement patterns or types applied to entity types or token groups during re-identification. Specifies a replacement pattern/type and the token group or entity type it applies to during re-identification. Set tokenGroupName for FlowVault and entityName for SkyVault. Similarly, set redactionType for FlowVault and redactionPattern for SkyVaultto specify the replacement.

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + public _FinalStage redactionLevel(List redactionLevel) { + this.redactionLevel = Optional.ofNullable(redactionLevel); + return this; + } + + /** + *

Replacement patterns or types applied to entity types or token groups during re-identification. Specifies a replacement pattern/type and the token group or entity type it applies to during re-identification. Set tokenGroupName for FlowVault and entityName for SkyVault. Similarly, set redactionType for FlowVault and redactionPattern for SkyVaultto specify the replacement.

+ */ + @java.lang.Override + @JsonSetter(value = "redactionLevel", nulls = Nulls.SKIP) + public _FinalStage redactionLevel(Optional> redactionLevel) { + this.redactionLevel = redactionLevel; + return this; + } + + /** + *

Format of the input file. Limited to the formats that support the VAULT_TOKEN de-identification type.

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + public _FinalStage dataFormat(ReidentifyFileRequestV2DataFormat dataFormat) { + this.dataFormat = Optional.ofNullable(dataFormat); + return this; + } + + /** + *

Format of the input file. Limited to the formats that support the VAULT_TOKEN de-identification type.

+ */ + @java.lang.Override + @JsonSetter(value = "dataFormat", nulls = Nulls.SKIP) + public _FinalStage dataFormat(Optional dataFormat) { + this.dataFormat = dataFormat; + return this; + } + + @java.lang.Override + public ReidentifyFileRequestV2 build() { + return new ReidentifyFileRequestV2( + dataSource, value, dataFormat, vaultId, redactionLevel, additionalProperties); + } + + @java.lang.Override + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + @java.lang.Override + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/resources/filesv2/types/DeidentifyFileRequestV2DataFormat.java b/common/src/main/java/com/skyflow/generated/detect/rest/resources/filesv2/types/DeidentifyFileRequestV2DataFormat.java new file mode 100644 index 00000000..6a4aaf01 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/resources/filesv2/types/DeidentifyFileRequestV2DataFormat.java @@ -0,0 +1,301 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.resources.filesv2.types; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +public final class DeidentifyFileRequestV2DataFormat { + public static final DeidentifyFileRequestV2DataFormat BMP = new DeidentifyFileRequestV2DataFormat(Value.BMP, "bmp"); + + public static final DeidentifyFileRequestV2DataFormat PPTX = + new DeidentifyFileRequestV2DataFormat(Value.PPTX, "pptx"); + + public static final DeidentifyFileRequestV2DataFormat WAV = new DeidentifyFileRequestV2DataFormat(Value.WAV, "wav"); + + public static final DeidentifyFileRequestV2DataFormat DOC = new DeidentifyFileRequestV2DataFormat(Value.DOC, "doc"); + + public static final DeidentifyFileRequestV2DataFormat XML = new DeidentifyFileRequestV2DataFormat(Value.XML, "xml"); + + public static final DeidentifyFileRequestV2DataFormat ZIP = new DeidentifyFileRequestV2DataFormat(Value.ZIP, "zip"); + + public static final DeidentifyFileRequestV2DataFormat MP3 = new DeidentifyFileRequestV2DataFormat(Value.MP3, "mp3"); + + public static final DeidentifyFileRequestV2DataFormat PPT = new DeidentifyFileRequestV2DataFormat(Value.PPT, "ppt"); + + public static final DeidentifyFileRequestV2DataFormat JPEG = + new DeidentifyFileRequestV2DataFormat(Value.JPEG, "jpeg"); + + public static final DeidentifyFileRequestV2DataFormat TIFF = + new DeidentifyFileRequestV2DataFormat(Value.TIFF, "tiff"); + + public static final DeidentifyFileRequestV2DataFormat PDF = new DeidentifyFileRequestV2DataFormat(Value.PDF, "pdf"); + + public static final DeidentifyFileRequestV2DataFormat DOCX = + new DeidentifyFileRequestV2DataFormat(Value.DOCX, "docx"); + + public static final DeidentifyFileRequestV2DataFormat PNG = new DeidentifyFileRequestV2DataFormat(Value.PNG, "png"); + + public static final DeidentifyFileRequestV2DataFormat JSONL = + new DeidentifyFileRequestV2DataFormat(Value.JSONL, "jsonl"); + + public static final DeidentifyFileRequestV2DataFormat CSV = new DeidentifyFileRequestV2DataFormat(Value.CSV, "csv"); + + public static final DeidentifyFileRequestV2DataFormat JPG = new DeidentifyFileRequestV2DataFormat(Value.JPG, "jpg"); + + public static final DeidentifyFileRequestV2DataFormat DCM = new DeidentifyFileRequestV2DataFormat(Value.DCM, "dcm"); + + public static final DeidentifyFileRequestV2DataFormat JSON = + new DeidentifyFileRequestV2DataFormat(Value.JSON, "json"); + + public static final DeidentifyFileRequestV2DataFormat GIF = new DeidentifyFileRequestV2DataFormat(Value.GIF, "gif"); + + public static final DeidentifyFileRequestV2DataFormat TXT = new DeidentifyFileRequestV2DataFormat(Value.TXT, "txt"); + + public static final DeidentifyFileRequestV2DataFormat XLS = new DeidentifyFileRequestV2DataFormat(Value.XLS, "xls"); + + public static final DeidentifyFileRequestV2DataFormat XLSX = + new DeidentifyFileRequestV2DataFormat(Value.XLSX, "xlsx"); + + public static final DeidentifyFileRequestV2DataFormat TIF = new DeidentifyFileRequestV2DataFormat(Value.TIF, "tif"); + + private final Value value; + + private final String string; + + DeidentifyFileRequestV2DataFormat(Value value, String string) { + this.value = value; + this.string = string; + } + + public Value getEnumValue() { + return value; + } + + @java.lang.Override + @JsonValue + public String toString() { + return this.string; + } + + @java.lang.Override + public boolean equals(Object other) { + return (this == other) + || (other instanceof DeidentifyFileRequestV2DataFormat + && this.string.equals(((DeidentifyFileRequestV2DataFormat) other).string)); + } + + @java.lang.Override + public int hashCode() { + return this.string.hashCode(); + } + + public T visit(Visitor visitor) { + switch (value) { + case BMP: + return visitor.visitBmp(); + case PPTX: + return visitor.visitPptx(); + case WAV: + return visitor.visitWav(); + case DOC: + return visitor.visitDoc(); + case XML: + return visitor.visitXml(); + case ZIP: + return visitor.visitZip(); + case MP3: + return visitor.visitMp3(); + case PPT: + return visitor.visitPpt(); + case JPEG: + return visitor.visitJpeg(); + case TIFF: + return visitor.visitTiff(); + case PDF: + return visitor.visitPdf(); + case DOCX: + return visitor.visitDocx(); + case PNG: + return visitor.visitPng(); + case JSONL: + return visitor.visitJsonl(); + case CSV: + return visitor.visitCsv(); + case JPG: + return visitor.visitJpg(); + case DCM: + return visitor.visitDcm(); + case JSON: + return visitor.visitJson(); + case GIF: + return visitor.visitGif(); + case TXT: + return visitor.visitTxt(); + case XLS: + return visitor.visitXls(); + case XLSX: + return visitor.visitXlsx(); + case TIF: + return visitor.visitTif(); + case UNKNOWN: + default: + return visitor.visitUnknown(string); + } + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static DeidentifyFileRequestV2DataFormat valueOf(String value) { + switch (value) { + case "bmp": + return BMP; + case "pptx": + return PPTX; + case "wav": + return WAV; + case "doc": + return DOC; + case "xml": + return XML; + case "zip": + return ZIP; + case "mp3": + return MP3; + case "ppt": + return PPT; + case "jpeg": + return JPEG; + case "tiff": + return TIFF; + case "pdf": + return PDF; + case "docx": + return DOCX; + case "png": + return PNG; + case "jsonl": + return JSONL; + case "csv": + return CSV; + case "jpg": + return JPG; + case "dcm": + return DCM; + case "json": + return JSON; + case "gif": + return GIF; + case "txt": + return TXT; + case "xls": + return XLS; + case "xlsx": + return XLSX; + case "tif": + return TIF; + default: + return new DeidentifyFileRequestV2DataFormat(Value.UNKNOWN, value); + } + } + + public enum Value { + MP3, + + WAV, + + PDF, + + TXT, + + CSV, + + JSON, + + JPG, + + JPEG, + + TIF, + + TIFF, + + PNG, + + BMP, + + XLS, + + XLSX, + + DOC, + + DOCX, + + PPT, + + PPTX, + + XML, + + DCM, + + JSONL, + + ZIP, + + GIF, + + UNKNOWN + } + + public interface Visitor { + T visitMp3(); + + T visitWav(); + + T visitPdf(); + + T visitTxt(); + + T visitCsv(); + + T visitJson(); + + T visitJpg(); + + T visitJpeg(); + + T visitTif(); + + T visitTiff(); + + T visitPng(); + + T visitBmp(); + + T visitXls(); + + T visitXlsx(); + + T visitDoc(); + + T visitDocx(); + + T visitPpt(); + + T visitPptx(); + + T visitXml(); + + T visitDcm(); + + T visitJsonl(); + + T visitZip(); + + T visitGif(); + + T visitUnknown(String unknownType); + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/resources/filesv2/types/DeidentifyFileRequestV2DataSource.java b/common/src/main/java/com/skyflow/generated/detect/rest/resources/filesv2/types/DeidentifyFileRequestV2DataSource.java new file mode 100644 index 00000000..83c9aed3 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/resources/filesv2/types/DeidentifyFileRequestV2DataSource.java @@ -0,0 +1,97 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.resources.filesv2.types; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +public final class DeidentifyFileRequestV2DataSource { + public static final DeidentifyFileRequestV2DataSource BASE64 = + new DeidentifyFileRequestV2DataSource(Value.BASE64, "BASE64"); + + public static final DeidentifyFileRequestV2DataSource SKYFLOW_ID = + new DeidentifyFileRequestV2DataSource(Value.SKYFLOW_ID, "SKYFLOW_ID"); + + public static final DeidentifyFileRequestV2DataSource PRESIGNED_URL = + new DeidentifyFileRequestV2DataSource(Value.PRESIGNED_URL, "PRESIGNED_URL"); + + private final Value value; + + private final String string; + + DeidentifyFileRequestV2DataSource(Value value, String string) { + this.value = value; + this.string = string; + } + + public Value getEnumValue() { + return value; + } + + @java.lang.Override + @JsonValue + public String toString() { + return this.string; + } + + @java.lang.Override + public boolean equals(Object other) { + return (this == other) + || (other instanceof DeidentifyFileRequestV2DataSource + && this.string.equals(((DeidentifyFileRequestV2DataSource) other).string)); + } + + @java.lang.Override + public int hashCode() { + return this.string.hashCode(); + } + + public T visit(Visitor visitor) { + switch (value) { + case BASE64: + return visitor.visitBase64(); + case SKYFLOW_ID: + return visitor.visitSkyflowId(); + case PRESIGNED_URL: + return visitor.visitPresignedUrl(); + case UNKNOWN: + default: + return visitor.visitUnknown(string); + } + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static DeidentifyFileRequestV2DataSource valueOf(String value) { + switch (value) { + case "BASE64": + return BASE64; + case "SKYFLOW_ID": + return SKYFLOW_ID; + case "PRESIGNED_URL": + return PRESIGNED_URL; + default: + return new DeidentifyFileRequestV2DataSource(Value.UNKNOWN, value); + } + } + + public enum Value { + BASE64, + + SKYFLOW_ID, + + PRESIGNED_URL, + + UNKNOWN + } + + public interface Visitor { + T visitBase64(); + + T visitSkyflowId(); + + T visitPresignedUrl(); + + T visitUnknown(String unknownType); + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/resources/filesv2/types/ReidentifyFileRequestV2DataFormat.java b/common/src/main/java/com/skyflow/generated/detect/rest/resources/filesv2/types/ReidentifyFileRequestV2DataFormat.java new file mode 100644 index 00000000..df3c688f --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/resources/filesv2/types/ReidentifyFileRequestV2DataFormat.java @@ -0,0 +1,149 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.resources.filesv2.types; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +public final class ReidentifyFileRequestV2DataFormat { + public static final ReidentifyFileRequestV2DataFormat PPTX = + new ReidentifyFileRequestV2DataFormat(Value.PPTX, "pptx"); + + public static final ReidentifyFileRequestV2DataFormat JSONL = + new ReidentifyFileRequestV2DataFormat(Value.JSONL, "jsonl"); + + public static final ReidentifyFileRequestV2DataFormat CSV = new ReidentifyFileRequestV2DataFormat(Value.CSV, "csv"); + + public static final ReidentifyFileRequestV2DataFormat XML = new ReidentifyFileRequestV2DataFormat(Value.XML, "xml"); + + public static final ReidentifyFileRequestV2DataFormat JSON = + new ReidentifyFileRequestV2DataFormat(Value.JSON, "json"); + + public static final ReidentifyFileRequestV2DataFormat TXT = new ReidentifyFileRequestV2DataFormat(Value.TXT, "txt"); + + public static final ReidentifyFileRequestV2DataFormat DOCX = + new ReidentifyFileRequestV2DataFormat(Value.DOCX, "docx"); + + public static final ReidentifyFileRequestV2DataFormat XLSX = + new ReidentifyFileRequestV2DataFormat(Value.XLSX, "xlsx"); + + private final Value value; + + private final String string; + + ReidentifyFileRequestV2DataFormat(Value value, String string) { + this.value = value; + this.string = string; + } + + public Value getEnumValue() { + return value; + } + + @java.lang.Override + @JsonValue + public String toString() { + return this.string; + } + + @java.lang.Override + public boolean equals(Object other) { + return (this == other) + || (other instanceof ReidentifyFileRequestV2DataFormat + && this.string.equals(((ReidentifyFileRequestV2DataFormat) other).string)); + } + + @java.lang.Override + public int hashCode() { + return this.string.hashCode(); + } + + public T visit(Visitor visitor) { + switch (value) { + case PPTX: + return visitor.visitPptx(); + case JSONL: + return visitor.visitJsonl(); + case CSV: + return visitor.visitCsv(); + case XML: + return visitor.visitXml(); + case JSON: + return visitor.visitJson(); + case TXT: + return visitor.visitTxt(); + case DOCX: + return visitor.visitDocx(); + case XLSX: + return visitor.visitXlsx(); + case UNKNOWN: + default: + return visitor.visitUnknown(string); + } + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static ReidentifyFileRequestV2DataFormat valueOf(String value) { + switch (value) { + case "pptx": + return PPTX; + case "jsonl": + return JSONL; + case "csv": + return CSV; + case "xml": + return XML; + case "json": + return JSON; + case "txt": + return TXT; + case "docx": + return DOCX; + case "xlsx": + return XLSX; + default: + return new ReidentifyFileRequestV2DataFormat(Value.UNKNOWN, value); + } + } + + public enum Value { + TXT, + + CSV, + + JSON, + + XLSX, + + DOCX, + + PPTX, + + XML, + + JSONL, + + UNKNOWN + } + + public interface Visitor { + T visitTxt(); + + T visitCsv(); + + T visitJson(); + + T visitXlsx(); + + T visitDocx(); + + T visitPptx(); + + T visitXml(); + + T visitJsonl(); + + T visitUnknown(String unknownType); + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/resources/filesv2/types/ReidentifyFileRequestV2DataSource.java b/common/src/main/java/com/skyflow/generated/detect/rest/resources/filesv2/types/ReidentifyFileRequestV2DataSource.java new file mode 100644 index 00000000..538cbc49 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/resources/filesv2/types/ReidentifyFileRequestV2DataSource.java @@ -0,0 +1,97 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.resources.filesv2.types; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +public final class ReidentifyFileRequestV2DataSource { + public static final ReidentifyFileRequestV2DataSource BASE64 = + new ReidentifyFileRequestV2DataSource(Value.BASE64, "BASE64"); + + public static final ReidentifyFileRequestV2DataSource SKYFLOW_ID = + new ReidentifyFileRequestV2DataSource(Value.SKYFLOW_ID, "SKYFLOW_ID"); + + public static final ReidentifyFileRequestV2DataSource PRESIGNED_URL = + new ReidentifyFileRequestV2DataSource(Value.PRESIGNED_URL, "PRESIGNED_URL"); + + private final Value value; + + private final String string; + + ReidentifyFileRequestV2DataSource(Value value, String string) { + this.value = value; + this.string = string; + } + + public Value getEnumValue() { + return value; + } + + @java.lang.Override + @JsonValue + public String toString() { + return this.string; + } + + @java.lang.Override + public boolean equals(Object other) { + return (this == other) + || (other instanceof ReidentifyFileRequestV2DataSource + && this.string.equals(((ReidentifyFileRequestV2DataSource) other).string)); + } + + @java.lang.Override + public int hashCode() { + return this.string.hashCode(); + } + + public T visit(Visitor visitor) { + switch (value) { + case BASE64: + return visitor.visitBase64(); + case SKYFLOW_ID: + return visitor.visitSkyflowId(); + case PRESIGNED_URL: + return visitor.visitPresignedUrl(); + case UNKNOWN: + default: + return visitor.visitUnknown(string); + } + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static ReidentifyFileRequestV2DataSource valueOf(String value) { + switch (value) { + case "BASE64": + return BASE64; + case "SKYFLOW_ID": + return SKYFLOW_ID; + case "PRESIGNED_URL": + return PRESIGNED_URL; + default: + return new ReidentifyFileRequestV2DataSource(Value.UNKNOWN, value); + } + } + + public enum Value { + BASE64, + + SKYFLOW_ID, + + PRESIGNED_URL, + + UNKNOWN + } + + public interface Visitor { + T visitBase64(); + + T visitSkyflowId(); + + T visitPresignedUrl(); + + T visitUnknown(String unknownType); + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/resources/guardrailsv2/AsyncGuardrailsV2Client.java b/common/src/main/java/com/skyflow/generated/detect/rest/resources/guardrailsv2/AsyncGuardrailsV2Client.java new file mode 100644 index 00000000..fe74a2d3 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/resources/guardrailsv2/AsyncGuardrailsV2Client.java @@ -0,0 +1,43 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.resources.guardrailsv2; + +import com.skyflow.generated.detect.rest.core.ClientOptions; +import com.skyflow.generated.detect.rest.core.RequestOptions; +import com.skyflow.generated.detect.rest.resources.guardrailsv2.requests.DetectGuardrailsRequestV2; +import com.skyflow.generated.detect.rest.types.DetectGuardrailsResponseV2; +import java.util.concurrent.CompletableFuture; + +public class AsyncGuardrailsV2Client { + protected final ClientOptions clientOptions; + + private final AsyncRawGuardrailsV2Client rawClient; + + public AsyncGuardrailsV2Client(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + this.rawClient = new AsyncRawGuardrailsV2Client(clientOptions); + } + + /** + * Get responses with HTTP metadata like headers + */ + public AsyncRawGuardrailsV2Client withRawResponse() { + return this.rawClient; + } + + /** + * Preserve safety and compliance with usage policies.

Note: V2 API is in beta. + */ + public CompletableFuture checkGuardrailsV2(DetectGuardrailsRequestV2 request) { + return this.rawClient.checkGuardrailsV2(request).thenApply(response -> response.body()); + } + + /** + * Preserve safety and compliance with usage policies.

Note: V2 API is in beta. + */ + public CompletableFuture checkGuardrailsV2( + DetectGuardrailsRequestV2 request, RequestOptions requestOptions) { + return this.rawClient.checkGuardrailsV2(request, requestOptions).thenApply(response -> response.body()); + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/resources/guardrailsv2/AsyncRawGuardrailsV2Client.java b/common/src/main/java/com/skyflow/generated/detect/rest/resources/guardrailsv2/AsyncRawGuardrailsV2Client.java new file mode 100644 index 00000000..c6b84c0e --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/resources/guardrailsv2/AsyncRawGuardrailsV2Client.java @@ -0,0 +1,142 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.resources.guardrailsv2; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.skyflow.generated.detect.rest.core.ApiClientApiException; +import com.skyflow.generated.detect.rest.core.ApiClientException; +import com.skyflow.generated.detect.rest.core.ApiClientHttpResponse; +import com.skyflow.generated.detect.rest.core.ClientOptions; +import com.skyflow.generated.detect.rest.core.MediaTypes; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import com.skyflow.generated.detect.rest.core.RequestOptions; +import com.skyflow.generated.detect.rest.core.RetryInterceptor; +import com.skyflow.generated.detect.rest.errors.BadRequestError; +import com.skyflow.generated.detect.rest.errors.InternalServerError; +import com.skyflow.generated.detect.rest.errors.UnauthorizedError; +import com.skyflow.generated.detect.rest.resources.guardrailsv2.requests.DetectGuardrailsRequestV2; +import com.skyflow.generated.detect.rest.types.DetectGuardrailsResponseV2; +import com.skyflow.generated.detect.rest.types.ErrorResponse; +import java.io.IOException; +import java.util.concurrent.CompletableFuture; +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.Headers; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.ResponseBody; +import org.jetbrains.annotations.NotNull; + +public class AsyncRawGuardrailsV2Client { + protected final ClientOptions clientOptions; + + public AsyncRawGuardrailsV2Client(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + } + + /** + * Preserve safety and compliance with usage policies.

Note: V2 API is in beta. + */ + public CompletableFuture> checkGuardrailsV2( + DetectGuardrailsRequestV2 request) { + return checkGuardrailsV2(request, null); + } + + /** + * Preserve safety and compliance with usage policies.

Note: V2 API is in beta. + */ + public CompletableFuture> checkGuardrailsV2( + DetectGuardrailsRequestV2 request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("v2/detect/guardrails"); + if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + }); + } + RequestBody body; + try { + body = RequestBody.create( + ObjectMappers.JSON_MAPPER.writeValueAsBytes(request), MediaTypes.APPLICATION_JSON); + } catch (JsonProcessingException e) { + throw new ApiClientException("Failed to serialize request", e); + } + Request okhttpRequest = new Request.Builder() + .url(httpUrl.build()) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .addHeader("Content-Type", "application/json") + .addHeader("Accept", "application/json") + .build(); + OkHttpClient client = clientOptions.httpClient(); + if (requestOptions != null && requestOptions.getTimeout().isPresent()) { + client = clientOptions.httpClientWithTimeout(requestOptions); + } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } + CompletableFuture> future = new CompletableFuture<>(); + client.newCall(okhttpRequest).enqueue(new Callback() { + @Override + public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { + try (ResponseBody responseBody = response.body()) { + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + future.complete(new ApiClientHttpResponse<>( + ObjectMappers.JSON_MAPPER.readValue( + responseBodyString, DetectGuardrailsResponseV2.class), + response)); + return; + } + try { + switch (response.code()) { + case 400: + future.completeExceptionally(new BadRequestError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), + response)); + return; + case 401: + future.completeExceptionally(new UnauthorizedError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), + response)); + return; + case 500: + future.completeExceptionally(new InternalServerError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), + response)); + return; + } + } catch (JsonProcessingException ignored) { + // unable to map error response, throwing generic error + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + future.completeExceptionally(new ApiClientApiException( + "Error with status code " + response.code(), response.code(), errorBody, response)); + return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new ApiClientException("Failed to deserialize response: " + e.getMessage(), e)); + } catch (IOException e) { + future.completeExceptionally(new ApiClientException("Network error executing HTTP request", e)); + } + } + + @Override + public void onFailure(@NotNull Call call, @NotNull IOException e) { + future.completeExceptionally(new ApiClientException("Network error executing HTTP request", e)); + } + }); + return future; + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/resources/guardrailsv2/GuardrailsV2Client.java b/common/src/main/java/com/skyflow/generated/detect/rest/resources/guardrailsv2/GuardrailsV2Client.java new file mode 100644 index 00000000..2d3a9bf7 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/resources/guardrailsv2/GuardrailsV2Client.java @@ -0,0 +1,42 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.resources.guardrailsv2; + +import com.skyflow.generated.detect.rest.core.ClientOptions; +import com.skyflow.generated.detect.rest.core.RequestOptions; +import com.skyflow.generated.detect.rest.resources.guardrailsv2.requests.DetectGuardrailsRequestV2; +import com.skyflow.generated.detect.rest.types.DetectGuardrailsResponseV2; + +public class GuardrailsV2Client { + protected final ClientOptions clientOptions; + + private final RawGuardrailsV2Client rawClient; + + public GuardrailsV2Client(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + this.rawClient = new RawGuardrailsV2Client(clientOptions); + } + + /** + * Get responses with HTTP metadata like headers + */ + public RawGuardrailsV2Client withRawResponse() { + return this.rawClient; + } + + /** + * Preserve safety and compliance with usage policies.

Note: V2 API is in beta. + */ + public DetectGuardrailsResponseV2 checkGuardrailsV2(DetectGuardrailsRequestV2 request) { + return this.rawClient.checkGuardrailsV2(request).body(); + } + + /** + * Preserve safety and compliance with usage policies.

Note: V2 API is in beta. + */ + public DetectGuardrailsResponseV2 checkGuardrailsV2( + DetectGuardrailsRequestV2 request, RequestOptions requestOptions) { + return this.rawClient.checkGuardrailsV2(request, requestOptions).body(); + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/resources/guardrailsv2/RawGuardrailsV2Client.java b/common/src/main/java/com/skyflow/generated/detect/rest/resources/guardrailsv2/RawGuardrailsV2Client.java new file mode 100644 index 00000000..c124c516 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/resources/guardrailsv2/RawGuardrailsV2Client.java @@ -0,0 +1,116 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.resources.guardrailsv2; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.skyflow.generated.detect.rest.core.ApiClientApiException; +import com.skyflow.generated.detect.rest.core.ApiClientException; +import com.skyflow.generated.detect.rest.core.ApiClientHttpResponse; +import com.skyflow.generated.detect.rest.core.ClientOptions; +import com.skyflow.generated.detect.rest.core.MediaTypes; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import com.skyflow.generated.detect.rest.core.RequestOptions; +import com.skyflow.generated.detect.rest.core.RetryInterceptor; +import com.skyflow.generated.detect.rest.errors.BadRequestError; +import com.skyflow.generated.detect.rest.errors.InternalServerError; +import com.skyflow.generated.detect.rest.errors.UnauthorizedError; +import com.skyflow.generated.detect.rest.resources.guardrailsv2.requests.DetectGuardrailsRequestV2; +import com.skyflow.generated.detect.rest.types.DetectGuardrailsResponseV2; +import com.skyflow.generated.detect.rest.types.ErrorResponse; +import java.io.IOException; +import okhttp3.Headers; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.ResponseBody; + +public class RawGuardrailsV2Client { + protected final ClientOptions clientOptions; + + public RawGuardrailsV2Client(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + } + + /** + * Preserve safety and compliance with usage policies.

Note: V2 API is in beta. + */ + public ApiClientHttpResponse checkGuardrailsV2(DetectGuardrailsRequestV2 request) { + return checkGuardrailsV2(request, null); + } + + /** + * Preserve safety and compliance with usage policies.

Note: V2 API is in beta. + */ + public ApiClientHttpResponse checkGuardrailsV2( + DetectGuardrailsRequestV2 request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("v2/detect/guardrails"); + if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + }); + } + RequestBody body; + try { + body = RequestBody.create( + ObjectMappers.JSON_MAPPER.writeValueAsBytes(request), MediaTypes.APPLICATION_JSON); + } catch (JsonProcessingException e) { + throw new ApiClientException("Failed to serialize request", e); + } + Request okhttpRequest = new Request.Builder() + .url(httpUrl.build()) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .addHeader("Content-Type", "application/json") + .addHeader("Accept", "application/json") + .build(); + OkHttpClient client = clientOptions.httpClient(); + if (requestOptions != null && requestOptions.getTimeout().isPresent()) { + client = clientOptions.httpClientWithTimeout(requestOptions); + } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } + try (Response response = client.newCall(okhttpRequest).execute()) { + ResponseBody responseBody = response.body(); + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + return new ApiClientHttpResponse<>( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, DetectGuardrailsResponseV2.class), + response); + } + try { + switch (response.code()) { + case 400: + throw new BadRequestError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), response); + case 401: + throw new UnauthorizedError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), response); + case 500: + throw new InternalServerError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), response); + } + } catch (JsonProcessingException ignored) { + // unable to map error response, throwing generic error + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + throw new ApiClientApiException( + "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new ApiClientException("Failed to deserialize response: " + e.getMessage(), e); + } catch (IOException e) { + throw new ApiClientException("Network error executing HTTP request", e); + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/resources/guardrailsv2/requests/DetectGuardrailsRequestV2.java b/common/src/main/java/com/skyflow/generated/detect/rest/resources/guardrailsv2/requests/DetectGuardrailsRequestV2.java new file mode 100644 index 00000000..f6d8dbad --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/resources/guardrailsv2/requests/DetectGuardrailsRequestV2.java @@ -0,0 +1,253 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.resources.guardrailsv2.requests; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import org.jetbrains.annotations.NotNull; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = DetectGuardrailsRequestV2.Builder.class) +public final class DetectGuardrailsRequestV2 { + private final String text; + + private final Optional checkToxicity; + + private final Optional> denyTopics; + + private final String vaultId; + + private final Map additionalProperties; + + private DetectGuardrailsRequestV2( + String text, + Optional checkToxicity, + Optional> denyTopics, + String vaultId, + Map additionalProperties) { + this.text = text; + this.checkToxicity = checkToxicity; + this.denyTopics = denyTopics; + this.vaultId = vaultId; + this.additionalProperties = additionalProperties; + } + + /** + * @return Text to check against guardrails. + */ + @JsonProperty("text") + public String getText() { + return text; + } + + /** + * @return If true, checks for toxicity in the text. + */ + @JsonProperty("checkToxicity") + public Optional getCheckToxicity() { + return checkToxicity; + } + + /** + * @return List of topics to deny. + */ + @JsonProperty("denyTopics") + public Optional> getDenyTopics() { + return denyTopics; + } + + /** + * @return ID of the vault. + */ + @JsonProperty("vaultId") + public String getVaultId() { + return vaultId; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof DetectGuardrailsRequestV2 && equalTo((DetectGuardrailsRequestV2) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(DetectGuardrailsRequestV2 other) { + return text.equals(other.text) + && checkToxicity.equals(other.checkToxicity) + && denyTopics.equals(other.denyTopics) + && vaultId.equals(other.vaultId); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.text, this.checkToxicity, this.denyTopics, this.vaultId); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static TextStage builder() { + return new Builder(); + } + + public interface TextStage { + /** + *

Text to check against guardrails.

+ */ + VaultIdStage text(@NotNull String text); + + Builder from(DetectGuardrailsRequestV2 other); + } + + public interface VaultIdStage { + /** + *

ID of the vault.

+ */ + _FinalStage vaultId(@NotNull String vaultId); + } + + public interface _FinalStage { + DetectGuardrailsRequestV2 build(); + + _FinalStage additionalProperty(String key, Object value); + + _FinalStage additionalProperties(Map additionalProperties); + + /** + *

If true, checks for toxicity in the text.

+ */ + _FinalStage checkToxicity(Optional checkToxicity); + + _FinalStage checkToxicity(Boolean checkToxicity); + + /** + *

List of topics to deny.

+ */ + _FinalStage denyTopics(Optional> denyTopics); + + _FinalStage denyTopics(List denyTopics); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder implements TextStage, VaultIdStage, _FinalStage { + private String text; + + private String vaultId; + + private Optional> denyTopics = Optional.empty(); + + private Optional checkToxicity = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + @java.lang.Override + public Builder from(DetectGuardrailsRequestV2 other) { + text(other.getText()); + checkToxicity(other.getCheckToxicity()); + denyTopics(other.getDenyTopics()); + vaultId(other.getVaultId()); + return this; + } + + /** + *

Text to check against guardrails.

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonSetter("text") + public VaultIdStage text(@NotNull String text) { + this.text = Objects.requireNonNull(text, "text must not be null"); + return this; + } + + /** + *

ID of the vault.

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonSetter("vaultId") + public _FinalStage vaultId(@NotNull String vaultId) { + this.vaultId = Objects.requireNonNull(vaultId, "vaultId must not be null"); + return this; + } + + /** + *

List of topics to deny.

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + public _FinalStage denyTopics(List denyTopics) { + this.denyTopics = Optional.ofNullable(denyTopics); + return this; + } + + /** + *

List of topics to deny.

+ */ + @java.lang.Override + @JsonSetter(value = "denyTopics", nulls = Nulls.SKIP) + public _FinalStage denyTopics(Optional> denyTopics) { + this.denyTopics = denyTopics; + return this; + } + + /** + *

If true, checks for toxicity in the text.

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + public _FinalStage checkToxicity(Boolean checkToxicity) { + this.checkToxicity = Optional.ofNullable(checkToxicity); + return this; + } + + /** + *

If true, checks for toxicity in the text.

+ */ + @java.lang.Override + @JsonSetter(value = "checkToxicity", nulls = Nulls.SKIP) + public _FinalStage checkToxicity(Optional checkToxicity) { + this.checkToxicity = checkToxicity; + return this; + } + + @java.lang.Override + public DetectGuardrailsRequestV2 build() { + return new DetectGuardrailsRequestV2(text, checkToxicity, denyTopics, vaultId, additionalProperties); + } + + @java.lang.Override + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + @java.lang.Override + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/resources/stringsv2/AsyncRawStringsV2Client.java b/common/src/main/java/com/skyflow/generated/detect/rest/resources/stringsv2/AsyncRawStringsV2Client.java new file mode 100644 index 00000000..cac69231 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/resources/stringsv2/AsyncRawStringsV2Client.java @@ -0,0 +1,246 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.resources.stringsv2; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.skyflow.generated.detect.rest.core.ApiClientApiException; +import com.skyflow.generated.detect.rest.core.ApiClientException; +import com.skyflow.generated.detect.rest.core.ApiClientHttpResponse; +import com.skyflow.generated.detect.rest.core.ClientOptions; +import com.skyflow.generated.detect.rest.core.MediaTypes; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import com.skyflow.generated.detect.rest.core.RequestOptions; +import com.skyflow.generated.detect.rest.core.RetryInterceptor; +import com.skyflow.generated.detect.rest.errors.BadRequestError; +import com.skyflow.generated.detect.rest.errors.InternalServerError; +import com.skyflow.generated.detect.rest.errors.UnauthorizedError; +import com.skyflow.generated.detect.rest.resources.stringsv2.requests.DeidentifyStringRequestV2; +import com.skyflow.generated.detect.rest.resources.stringsv2.requests.ReidentifyStringRequestV2; +import com.skyflow.generated.detect.rest.types.DeidentifyStringResponseV2; +import com.skyflow.generated.detect.rest.types.ErrorResponse; +import com.skyflow.generated.detect.rest.types.ReidentifyStringResponseV2; +import java.io.IOException; +import java.util.concurrent.CompletableFuture; +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.Headers; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.ResponseBody; +import org.jetbrains.annotations.NotNull; + +public class AsyncRawStringsV2Client { + protected final ClientOptions clientOptions; + + public AsyncRawStringsV2Client(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + } + + /** + * De-identifies sensitive data from a string.

Note: V2 API is in beta. + */ + public CompletableFuture> deidentifyStringV2( + DeidentifyStringRequestV2 request) { + return deidentifyStringV2(request, null); + } + + /** + * De-identifies sensitive data from a string.

Note: V2 API is in beta. + */ + public CompletableFuture> deidentifyStringV2( + DeidentifyStringRequestV2 request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("v2/detect/deidentify/string"); + if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + }); + } + RequestBody body; + try { + body = RequestBody.create( + ObjectMappers.JSON_MAPPER.writeValueAsBytes(request), MediaTypes.APPLICATION_JSON); + } catch (JsonProcessingException e) { + throw new ApiClientException("Failed to serialize request", e); + } + Request okhttpRequest = new Request.Builder() + .url(httpUrl.build()) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .addHeader("Content-Type", "application/json") + .addHeader("Accept", "application/json") + .build(); + OkHttpClient client = clientOptions.httpClient(); + if (requestOptions != null && requestOptions.getTimeout().isPresent()) { + client = clientOptions.httpClientWithTimeout(requestOptions); + } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } + CompletableFuture> future = new CompletableFuture<>(); + client.newCall(okhttpRequest).enqueue(new Callback() { + @Override + public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { + try (ResponseBody responseBody = response.body()) { + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + future.complete(new ApiClientHttpResponse<>( + ObjectMappers.JSON_MAPPER.readValue( + responseBodyString, DeidentifyStringResponseV2.class), + response)); + return; + } + try { + switch (response.code()) { + case 400: + future.completeExceptionally(new BadRequestError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), + response)); + return; + case 401: + future.completeExceptionally(new UnauthorizedError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), + response)); + return; + case 500: + future.completeExceptionally(new InternalServerError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), + response)); + return; + } + } catch (JsonProcessingException ignored) { + // unable to map error response, throwing generic error + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + future.completeExceptionally(new ApiClientApiException( + "Error with status code " + response.code(), response.code(), errorBody, response)); + return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new ApiClientException("Failed to deserialize response: " + e.getMessage(), e)); + } catch (IOException e) { + future.completeExceptionally(new ApiClientException("Network error executing HTTP request", e)); + } + } + + @Override + public void onFailure(@NotNull Call call, @NotNull IOException e) { + future.completeExceptionally(new ApiClientException("Network error executing HTTP request", e)); + } + }); + return future; + } + + /** + * Re-identifies tokens in a string.

Requires the Detect Invoker, Detect Manager, or Vault Owner role. Re-identifying vault_token values also requires a policy that separately grants the Detokenization action, unless the caller has the Vault Owner role.

Note: V2 API is in beta. + */ + public CompletableFuture> reidentifyStringV2( + ReidentifyStringRequestV2 request) { + return reidentifyStringV2(request, null); + } + + /** + * Re-identifies tokens in a string.

Requires the Detect Invoker, Detect Manager, or Vault Owner role. Re-identifying vault_token values also requires a policy that separately grants the Detokenization action, unless the caller has the Vault Owner role.

Note: V2 API is in beta. + */ + public CompletableFuture> reidentifyStringV2( + ReidentifyStringRequestV2 request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("v2/detect/reidentify/string"); + if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + }); + } + RequestBody body; + try { + body = RequestBody.create( + ObjectMappers.JSON_MAPPER.writeValueAsBytes(request), MediaTypes.APPLICATION_JSON); + } catch (JsonProcessingException e) { + throw new ApiClientException("Failed to serialize request", e); + } + Request okhttpRequest = new Request.Builder() + .url(httpUrl.build()) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .addHeader("Content-Type", "application/json") + .addHeader("Accept", "application/json") + .build(); + OkHttpClient client = clientOptions.httpClient(); + if (requestOptions != null && requestOptions.getTimeout().isPresent()) { + client = clientOptions.httpClientWithTimeout(requestOptions); + } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } + CompletableFuture> future = new CompletableFuture<>(); + client.newCall(okhttpRequest).enqueue(new Callback() { + @Override + public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { + try (ResponseBody responseBody = response.body()) { + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + future.complete(new ApiClientHttpResponse<>( + ObjectMappers.JSON_MAPPER.readValue( + responseBodyString, ReidentifyStringResponseV2.class), + response)); + return; + } + try { + switch (response.code()) { + case 400: + future.completeExceptionally(new BadRequestError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), + response)); + return; + case 401: + future.completeExceptionally(new UnauthorizedError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), + response)); + return; + case 500: + future.completeExceptionally(new InternalServerError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), + response)); + return; + } + } catch (JsonProcessingException ignored) { + // unable to map error response, throwing generic error + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + future.completeExceptionally(new ApiClientApiException( + "Error with status code " + response.code(), response.code(), errorBody, response)); + return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new ApiClientException("Failed to deserialize response: " + e.getMessage(), e)); + } catch (IOException e) { + future.completeExceptionally(new ApiClientException("Network error executing HTTP request", e)); + } + } + + @Override + public void onFailure(@NotNull Call call, @NotNull IOException e) { + future.completeExceptionally(new ApiClientException("Network error executing HTTP request", e)); + } + }); + return future; + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/resources/stringsv2/AsyncStringsV2Client.java b/common/src/main/java/com/skyflow/generated/detect/rest/resources/stringsv2/AsyncStringsV2Client.java new file mode 100644 index 00000000..47c34f06 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/resources/stringsv2/AsyncStringsV2Client.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.resources.stringsv2; + +import com.skyflow.generated.detect.rest.core.ClientOptions; +import com.skyflow.generated.detect.rest.core.RequestOptions; +import com.skyflow.generated.detect.rest.resources.stringsv2.requests.DeidentifyStringRequestV2; +import com.skyflow.generated.detect.rest.resources.stringsv2.requests.ReidentifyStringRequestV2; +import com.skyflow.generated.detect.rest.types.DeidentifyStringResponseV2; +import com.skyflow.generated.detect.rest.types.ReidentifyStringResponseV2; +import java.util.concurrent.CompletableFuture; + +public class AsyncStringsV2Client { + protected final ClientOptions clientOptions; + + private final AsyncRawStringsV2Client rawClient; + + public AsyncStringsV2Client(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + this.rawClient = new AsyncRawStringsV2Client(clientOptions); + } + + /** + * Get responses with HTTP metadata like headers + */ + public AsyncRawStringsV2Client withRawResponse() { + return this.rawClient; + } + + /** + * De-identifies sensitive data from a string.

Note: V2 API is in beta. + */ + public CompletableFuture deidentifyStringV2(DeidentifyStringRequestV2 request) { + return this.rawClient.deidentifyStringV2(request).thenApply(response -> response.body()); + } + + /** + * De-identifies sensitive data from a string.

Note: V2 API is in beta. + */ + public CompletableFuture deidentifyStringV2( + DeidentifyStringRequestV2 request, RequestOptions requestOptions) { + return this.rawClient.deidentifyStringV2(request, requestOptions).thenApply(response -> response.body()); + } + + /** + * Re-identifies tokens in a string.

Requires the Detect Invoker, Detect Manager, or Vault Owner role. Re-identifying vault_token values also requires a policy that separately grants the Detokenization action, unless the caller has the Vault Owner role.

Note: V2 API is in beta. + */ + public CompletableFuture reidentifyStringV2(ReidentifyStringRequestV2 request) { + return this.rawClient.reidentifyStringV2(request).thenApply(response -> response.body()); + } + + /** + * Re-identifies tokens in a string.

Requires the Detect Invoker, Detect Manager, or Vault Owner role. Re-identifying vault_token values also requires a policy that separately grants the Detokenization action, unless the caller has the Vault Owner role.

Note: V2 API is in beta. + */ + public CompletableFuture reidentifyStringV2( + ReidentifyStringRequestV2 request, RequestOptions requestOptions) { + return this.rawClient.reidentifyStringV2(request, requestOptions).thenApply(response -> response.body()); + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/resources/stringsv2/RawStringsV2Client.java b/common/src/main/java/com/skyflow/generated/detect/rest/resources/stringsv2/RawStringsV2Client.java new file mode 100644 index 00000000..61f71478 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/resources/stringsv2/RawStringsV2Client.java @@ -0,0 +1,198 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.resources.stringsv2; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.skyflow.generated.detect.rest.core.ApiClientApiException; +import com.skyflow.generated.detect.rest.core.ApiClientException; +import com.skyflow.generated.detect.rest.core.ApiClientHttpResponse; +import com.skyflow.generated.detect.rest.core.ClientOptions; +import com.skyflow.generated.detect.rest.core.MediaTypes; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import com.skyflow.generated.detect.rest.core.RequestOptions; +import com.skyflow.generated.detect.rest.core.RetryInterceptor; +import com.skyflow.generated.detect.rest.errors.BadRequestError; +import com.skyflow.generated.detect.rest.errors.InternalServerError; +import com.skyflow.generated.detect.rest.errors.UnauthorizedError; +import com.skyflow.generated.detect.rest.resources.stringsv2.requests.DeidentifyStringRequestV2; +import com.skyflow.generated.detect.rest.resources.stringsv2.requests.ReidentifyStringRequestV2; +import com.skyflow.generated.detect.rest.types.DeidentifyStringResponseV2; +import com.skyflow.generated.detect.rest.types.ErrorResponse; +import com.skyflow.generated.detect.rest.types.ReidentifyStringResponseV2; +import java.io.IOException; +import okhttp3.Headers; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.ResponseBody; + +public class RawStringsV2Client { + protected final ClientOptions clientOptions; + + public RawStringsV2Client(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + } + + /** + * De-identifies sensitive data from a string.

Note: V2 API is in beta. + */ + public ApiClientHttpResponse deidentifyStringV2(DeidentifyStringRequestV2 request) { + return deidentifyStringV2(request, null); + } + + /** + * De-identifies sensitive data from a string.

Note: V2 API is in beta. + */ + public ApiClientHttpResponse deidentifyStringV2( + DeidentifyStringRequestV2 request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("v2/detect/deidentify/string"); + if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + }); + } + RequestBody body; + try { + body = RequestBody.create( + ObjectMappers.JSON_MAPPER.writeValueAsBytes(request), MediaTypes.APPLICATION_JSON); + } catch (JsonProcessingException e) { + throw new ApiClientException("Failed to serialize request", e); + } + Request okhttpRequest = new Request.Builder() + .url(httpUrl.build()) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .addHeader("Content-Type", "application/json") + .addHeader("Accept", "application/json") + .build(); + OkHttpClient client = clientOptions.httpClient(); + if (requestOptions != null && requestOptions.getTimeout().isPresent()) { + client = clientOptions.httpClientWithTimeout(requestOptions); + } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } + try (Response response = client.newCall(okhttpRequest).execute()) { + ResponseBody responseBody = response.body(); + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + return new ApiClientHttpResponse<>( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, DeidentifyStringResponseV2.class), + response); + } + try { + switch (response.code()) { + case 400: + throw new BadRequestError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), response); + case 401: + throw new UnauthorizedError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), response); + case 500: + throw new InternalServerError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), response); + } + } catch (JsonProcessingException ignored) { + // unable to map error response, throwing generic error + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + throw new ApiClientApiException( + "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new ApiClientException("Failed to deserialize response: " + e.getMessage(), e); + } catch (IOException e) { + throw new ApiClientException("Network error executing HTTP request", e); + } + } + + /** + * Re-identifies tokens in a string.

Requires the Detect Invoker, Detect Manager, or Vault Owner role. Re-identifying vault_token values also requires a policy that separately grants the Detokenization action, unless the caller has the Vault Owner role.

Note: V2 API is in beta. + */ + public ApiClientHttpResponse reidentifyStringV2(ReidentifyStringRequestV2 request) { + return reidentifyStringV2(request, null); + } + + /** + * Re-identifies tokens in a string.

Requires the Detect Invoker, Detect Manager, or Vault Owner role. Re-identifying vault_token values also requires a policy that separately grants the Detokenization action, unless the caller has the Vault Owner role.

Note: V2 API is in beta. + */ + public ApiClientHttpResponse reidentifyStringV2( + ReidentifyStringRequestV2 request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("v2/detect/reidentify/string"); + if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + }); + } + RequestBody body; + try { + body = RequestBody.create( + ObjectMappers.JSON_MAPPER.writeValueAsBytes(request), MediaTypes.APPLICATION_JSON); + } catch (JsonProcessingException e) { + throw new ApiClientException("Failed to serialize request", e); + } + Request okhttpRequest = new Request.Builder() + .url(httpUrl.build()) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .addHeader("Content-Type", "application/json") + .addHeader("Accept", "application/json") + .build(); + OkHttpClient client = clientOptions.httpClient(); + if (requestOptions != null && requestOptions.getTimeout().isPresent()) { + client = clientOptions.httpClientWithTimeout(requestOptions); + } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } + try (Response response = client.newCall(okhttpRequest).execute()) { + ResponseBody responseBody = response.body(); + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + return new ApiClientHttpResponse<>( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ReidentifyStringResponseV2.class), + response); + } + try { + switch (response.code()) { + case 400: + throw new BadRequestError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), response); + case 401: + throw new UnauthorizedError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), response); + case 500: + throw new InternalServerError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), response); + } + } catch (JsonProcessingException ignored) { + // unable to map error response, throwing generic error + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + throw new ApiClientApiException( + "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new ApiClientException("Failed to deserialize response: " + e.getMessage(), e); + } catch (IOException e) { + throw new ApiClientException("Network error executing HTTP request", e); + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/resources/stringsv2/StringsV2Client.java b/common/src/main/java/com/skyflow/generated/detect/rest/resources/stringsv2/StringsV2Client.java new file mode 100644 index 00000000..2a50d897 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/resources/stringsv2/StringsV2Client.java @@ -0,0 +1,59 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.resources.stringsv2; + +import com.skyflow.generated.detect.rest.core.ClientOptions; +import com.skyflow.generated.detect.rest.core.RequestOptions; +import com.skyflow.generated.detect.rest.resources.stringsv2.requests.DeidentifyStringRequestV2; +import com.skyflow.generated.detect.rest.resources.stringsv2.requests.ReidentifyStringRequestV2; +import com.skyflow.generated.detect.rest.types.DeidentifyStringResponseV2; +import com.skyflow.generated.detect.rest.types.ReidentifyStringResponseV2; + +public class StringsV2Client { + protected final ClientOptions clientOptions; + + private final RawStringsV2Client rawClient; + + public StringsV2Client(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + this.rawClient = new RawStringsV2Client(clientOptions); + } + + /** + * Get responses with HTTP metadata like headers + */ + public RawStringsV2Client withRawResponse() { + return this.rawClient; + } + + /** + * De-identifies sensitive data from a string.

Note: V2 API is in beta. + */ + public DeidentifyStringResponseV2 deidentifyStringV2(DeidentifyStringRequestV2 request) { + return this.rawClient.deidentifyStringV2(request).body(); + } + + /** + * De-identifies sensitive data from a string.

Note: V2 API is in beta. + */ + public DeidentifyStringResponseV2 deidentifyStringV2( + DeidentifyStringRequestV2 request, RequestOptions requestOptions) { + return this.rawClient.deidentifyStringV2(request, requestOptions).body(); + } + + /** + * Re-identifies tokens in a string.

Requires the Detect Invoker, Detect Manager, or Vault Owner role. Re-identifying vault_token values also requires a policy that separately grants the Detokenization action, unless the caller has the Vault Owner role.

Note: V2 API is in beta. + */ + public ReidentifyStringResponseV2 reidentifyStringV2(ReidentifyStringRequestV2 request) { + return this.rawClient.reidentifyStringV2(request).body(); + } + + /** + * Re-identifies tokens in a string.

Requires the Detect Invoker, Detect Manager, or Vault Owner role. Re-identifying vault_token values also requires a policy that separately grants the Detokenization action, unless the caller has the Vault Owner role.

Note: V2 API is in beta. + */ + public ReidentifyStringResponseV2 reidentifyStringV2( + ReidentifyStringRequestV2 request, RequestOptions requestOptions) { + return this.rawClient.reidentifyStringV2(request, requestOptions).body(); + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/resources/stringsv2/requests/DeidentifyStringRequestV2.java b/common/src/main/java/com/skyflow/generated/detect/rest/resources/stringsv2/requests/DeidentifyStringRequestV2.java new file mode 100644 index 00000000..c3964a6d --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/resources/stringsv2/requests/DeidentifyStringRequestV2.java @@ -0,0 +1,206 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.resources.stringsv2.requests; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import com.skyflow.generated.detect.rest.types.DetectConfigV2; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import org.jetbrains.annotations.NotNull; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = DeidentifyStringRequestV2.Builder.class) +public final class DeidentifyStringRequestV2 { + private final String text; + + private final Optional configurationId; + + private final Optional configuration; + + private final Map additionalProperties; + + private DeidentifyStringRequestV2( + String text, + Optional configurationId, + Optional configuration, + Map additionalProperties) { + this.text = text; + this.configurationId = configurationId; + this.configuration = configuration; + this.additionalProperties = additionalProperties; + } + + /** + * @return Text to deidentify. + */ + @JsonProperty("text") + public String getText() { + return text; + } + + /** + * @return ID of the Detect configuration to use for de-identification. + */ + @JsonProperty("configurationId") + public Optional getConfigurationId() { + return configurationId; + } + + @JsonProperty("configuration") + public Optional getConfiguration() { + return configuration; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof DeidentifyStringRequestV2 && equalTo((DeidentifyStringRequestV2) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(DeidentifyStringRequestV2 other) { + return text.equals(other.text) + && configurationId.equals(other.configurationId) + && configuration.equals(other.configuration); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.text, this.configurationId, this.configuration); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static TextStage builder() { + return new Builder(); + } + + public interface TextStage { + /** + *

Text to deidentify.

+ */ + _FinalStage text(@NotNull String text); + + Builder from(DeidentifyStringRequestV2 other); + } + + public interface _FinalStage { + DeidentifyStringRequestV2 build(); + + _FinalStage additionalProperty(String key, Object value); + + _FinalStage additionalProperties(Map additionalProperties); + + /** + *

ID of the Detect configuration to use for de-identification.

+ */ + _FinalStage configurationId(Optional configurationId); + + _FinalStage configurationId(String configurationId); + + _FinalStage configuration(Optional configuration); + + _FinalStage configuration(DetectConfigV2 configuration); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder implements TextStage, _FinalStage { + private String text; + + private Optional configuration = Optional.empty(); + + private Optional configurationId = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + @java.lang.Override + public Builder from(DeidentifyStringRequestV2 other) { + text(other.getText()); + configurationId(other.getConfigurationId()); + configuration(other.getConfiguration()); + return this; + } + + /** + *

Text to deidentify.

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonSetter("text") + public _FinalStage text(@NotNull String text) { + this.text = Objects.requireNonNull(text, "text must not be null"); + return this; + } + + @java.lang.Override + public _FinalStage configuration(DetectConfigV2 configuration) { + this.configuration = Optional.ofNullable(configuration); + return this; + } + + @java.lang.Override + @JsonSetter(value = "configuration", nulls = Nulls.SKIP) + public _FinalStage configuration(Optional configuration) { + this.configuration = configuration; + return this; + } + + /** + *

ID of the Detect configuration to use for de-identification.

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + public _FinalStage configurationId(String configurationId) { + this.configurationId = Optional.ofNullable(configurationId); + return this; + } + + /** + *

ID of the Detect configuration to use for de-identification.

+ */ + @java.lang.Override + @JsonSetter(value = "configurationId", nulls = Nulls.SKIP) + public _FinalStage configurationId(Optional configurationId) { + this.configurationId = configurationId; + return this; + } + + @java.lang.Override + public DeidentifyStringRequestV2 build() { + return new DeidentifyStringRequestV2(text, configurationId, configuration, additionalProperties); + } + + @java.lang.Override + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + @java.lang.Override + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/resources/stringsv2/requests/ReidentifyStringRequestV2.java b/common/src/main/java/com/skyflow/generated/detect/rest/resources/stringsv2/requests/ReidentifyStringRequestV2.java new file mode 100644 index 00000000..b242a87b --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/resources/stringsv2/requests/ReidentifyStringRequestV2.java @@ -0,0 +1,209 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.resources.stringsv2.requests; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import com.skyflow.generated.detect.rest.types.RedactionLevel; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import org.jetbrains.annotations.NotNull; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = ReidentifyStringRequestV2.Builder.class) +public final class ReidentifyStringRequestV2 { + private final String vaultId; + + private final String text; + + private final Optional> redactionLevel; + + private final Map additionalProperties; + + private ReidentifyStringRequestV2( + String vaultId, + String text, + Optional> redactionLevel, + Map additionalProperties) { + this.vaultId = vaultId; + this.text = text; + this.redactionLevel = redactionLevel; + this.additionalProperties = additionalProperties; + } + + /** + * @return ID of the vault used for re-identification. Requires the Detect Invoker, Detect Manager, or Vault Owner role for this vault. Re-identifying vault_token values also requires a role or policy that separately grants the Detokenization action, unless the caller has the Vault Owner role. + */ + @JsonProperty("vaultId") + public String getVaultId() { + return vaultId; + } + + /** + * @return Text to reidentify. + */ + @JsonProperty("text") + public String getText() { + return text; + } + + /** + * @return Replacement patterns or types applied to entity types or token groups during re-identification. Specifies a replacement pattern/type and the token group or entity type it applies to during re-identification. Set exactly one of tokenGroupName or entityName. Use tokenGroupName for FlowVault and entityName for SkyVault. Similarly, set exactly one of redactionType or redactionPattern to specify the replacement. + */ + @JsonProperty("redactionLevel") + public Optional> getRedactionLevel() { + return redactionLevel; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof ReidentifyStringRequestV2 && equalTo((ReidentifyStringRequestV2) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(ReidentifyStringRequestV2 other) { + return vaultId.equals(other.vaultId) && text.equals(other.text) && redactionLevel.equals(other.redactionLevel); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.vaultId, this.text, this.redactionLevel); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static VaultIdStage builder() { + return new Builder(); + } + + public interface VaultIdStage { + /** + *

ID of the vault used for re-identification. Requires the Detect Invoker, Detect Manager, or Vault Owner role for this vault. Re-identifying vault_token values also requires a role or policy that separately grants the Detokenization action, unless the caller has the Vault Owner role.

+ */ + TextStage vaultId(@NotNull String vaultId); + + Builder from(ReidentifyStringRequestV2 other); + } + + public interface TextStage { + /** + *

Text to reidentify.

+ */ + _FinalStage text(@NotNull String text); + } + + public interface _FinalStage { + ReidentifyStringRequestV2 build(); + + _FinalStage additionalProperty(String key, Object value); + + _FinalStage additionalProperties(Map additionalProperties); + + /** + *

Replacement patterns or types applied to entity types or token groups during re-identification. Specifies a replacement pattern/type and the token group or entity type it applies to during re-identification. Set exactly one of tokenGroupName or entityName. Use tokenGroupName for FlowVault and entityName for SkyVault. Similarly, set exactly one of redactionType or redactionPattern to specify the replacement.

+ */ + _FinalStage redactionLevel(Optional> redactionLevel); + + _FinalStage redactionLevel(List redactionLevel); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder implements VaultIdStage, TextStage, _FinalStage { + private String vaultId; + + private String text; + + private Optional> redactionLevel = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + @java.lang.Override + public Builder from(ReidentifyStringRequestV2 other) { + vaultId(other.getVaultId()); + text(other.getText()); + redactionLevel(other.getRedactionLevel()); + return this; + } + + /** + *

ID of the vault used for re-identification. Requires the Detect Invoker, Detect Manager, or Vault Owner role for this vault. Re-identifying vault_token values also requires a role or policy that separately grants the Detokenization action, unless the caller has the Vault Owner role.

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonSetter("vaultId") + public TextStage vaultId(@NotNull String vaultId) { + this.vaultId = Objects.requireNonNull(vaultId, "vaultId must not be null"); + return this; + } + + /** + *

Text to reidentify.

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonSetter("text") + public _FinalStage text(@NotNull String text) { + this.text = Objects.requireNonNull(text, "text must not be null"); + return this; + } + + /** + *

Replacement patterns or types applied to entity types or token groups during re-identification. Specifies a replacement pattern/type and the token group or entity type it applies to during re-identification. Set exactly one of tokenGroupName or entityName. Use tokenGroupName for FlowVault and entityName for SkyVault. Similarly, set exactly one of redactionType or redactionPattern to specify the replacement.

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + public _FinalStage redactionLevel(List redactionLevel) { + this.redactionLevel = Optional.ofNullable(redactionLevel); + return this; + } + + /** + *

Replacement patterns or types applied to entity types or token groups during re-identification. Specifies a replacement pattern/type and the token group or entity type it applies to during re-identification. Set exactly one of tokenGroupName or entityName. Use tokenGroupName for FlowVault and entityName for SkyVault. Similarly, set exactly one of redactionType or redactionPattern to specify the replacement.

+ */ + @java.lang.Override + @JsonSetter(value = "redactionLevel", nulls = Nulls.SKIP) + public _FinalStage redactionLevel(Optional> redactionLevel) { + this.redactionLevel = redactionLevel; + return this; + } + + @java.lang.Override + public ReidentifyStringRequestV2 build() { + return new ReidentifyStringRequestV2(vaultId, text, redactionLevel, additionalProperties); + } + + @java.lang.Override + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + @java.lang.Override + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/AudioV2.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/AudioV2.java new file mode 100644 index 00000000..eb4be5de --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/AudioV2.java @@ -0,0 +1,167 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = AudioV2.Builder.class) +public final class AudioV2 { + private final Optional outputProcessedAudio; + + private final Optional outputTranscription; + + private final Optional bleep; + + private final Map additionalProperties; + + private AudioV2( + Optional outputProcessedAudio, + Optional outputTranscription, + Optional bleep, + Map additionalProperties) { + this.outputProcessedAudio = outputProcessedAudio; + this.outputTranscription = outputTranscription; + this.bleep = bleep; + this.additionalProperties = additionalProperties; + } + + /** + * @return If true, includes processed audio file in the response. Can't be used together with outputTranscription. + */ + @JsonProperty("outputProcessedAudio") + public Optional getOutputProcessedAudio() { + return outputProcessedAudio; + } + + /** + * @return Type of transcription to include in the response. TRANSCRIPTION: standard transcript of the audio. MEDICAL_TRANSCRIPTION: transcript focused on medical content and terminology. DIARIZED_TRANSCRIPTION: speaker-separated transcript that labels each speaker's dialogue. MEDICAL_DIARIZED_TRANSCRIPTION: speaker-separated transcript focused on medical content and terminology. NONE: no transcription is included. Can't be used together with outputProcessedAudio. + */ + @JsonProperty("outputTranscription") + public Optional getOutputTranscription() { + return outputTranscription; + } + + @JsonProperty("bleep") + public Optional getBleep() { + return bleep; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof AudioV2 && equalTo((AudioV2) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(AudioV2 other) { + return outputProcessedAudio.equals(other.outputProcessedAudio) + && outputTranscription.equals(other.outputTranscription) + && bleep.equals(other.bleep); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.outputProcessedAudio, this.outputTranscription, this.bleep); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + private Optional outputProcessedAudio = Optional.empty(); + + private Optional outputTranscription = Optional.empty(); + + private Optional bleep = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(AudioV2 other) { + outputProcessedAudio(other.getOutputProcessedAudio()); + outputTranscription(other.getOutputTranscription()); + bleep(other.getBleep()); + return this; + } + + /** + *

If true, includes processed audio file in the response. Can't be used together with outputTranscription.

+ */ + @JsonSetter(value = "outputProcessedAudio", nulls = Nulls.SKIP) + public Builder outputProcessedAudio(Optional outputProcessedAudio) { + this.outputProcessedAudio = outputProcessedAudio; + return this; + } + + public Builder outputProcessedAudio(Boolean outputProcessedAudio) { + this.outputProcessedAudio = Optional.ofNullable(outputProcessedAudio); + return this; + } + + /** + *

Type of transcription to include in the response. TRANSCRIPTION: standard transcript of the audio. MEDICAL_TRANSCRIPTION: transcript focused on medical content and terminology. DIARIZED_TRANSCRIPTION: speaker-separated transcript that labels each speaker's dialogue. MEDICAL_DIARIZED_TRANSCRIPTION: speaker-separated transcript focused on medical content and terminology. NONE: no transcription is included. Can't be used together with outputProcessedAudio.

+ */ + @JsonSetter(value = "outputTranscription", nulls = Nulls.SKIP) + public Builder outputTranscription(Optional outputTranscription) { + this.outputTranscription = outputTranscription; + return this; + } + + public Builder outputTranscription(AudioV2OutputTranscription outputTranscription) { + this.outputTranscription = Optional.ofNullable(outputTranscription); + return this; + } + + @JsonSetter(value = "bleep", nulls = Nulls.SKIP) + public Builder bleep(Optional bleep) { + this.bleep = bleep; + return this; + } + + public Builder bleep(AudioV2Bleep bleep) { + this.bleep = Optional.ofNullable(bleep); + return this; + } + + public AudioV2 build() { + return new AudioV2(outputProcessedAudio, outputTranscription, bleep, additionalProperties); + } + + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/AudioV2Bleep.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/AudioV2Bleep.java new file mode 100644 index 00000000..eb234230 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/AudioV2Bleep.java @@ -0,0 +1,203 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = AudioV2Bleep.Builder.class) +public final class AudioV2Bleep { + private final Optional startPadding; + + private final Optional stopPadding; + + private final Optional frequency; + + private final Optional gain; + + private final Map additionalProperties; + + private AudioV2Bleep( + Optional startPadding, + Optional stopPadding, + Optional frequency, + Optional gain, + Map additionalProperties) { + this.startPadding = startPadding; + this.stopPadding = stopPadding; + this.frequency = frequency; + this.gain = gain; + this.additionalProperties = additionalProperties; + } + + /** + * @return Padding added to the beginning of a bleep, in seconds. Range: 0–20. Defaults to 0.5. + */ + @JsonProperty("startPadding") + public Optional getStartPadding() { + return startPadding; + } + + /** + * @return Padding added to the end of a bleep, in seconds. Range: 0–20. Defaults to 0.2. + */ + @JsonProperty("stopPadding") + public Optional getStopPadding() { + return stopPadding; + } + + /** + * @return Frequency of the sine wave used for the bleep sound, in Hz. + */ + @JsonProperty("frequency") + public Optional getFrequency() { + return frequency; + } + + /** + * @return Relative loudness of the bleep. Positive values increase loudness and negative values decrease it. + */ + @JsonProperty("gain") + public Optional getGain() { + return gain; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof AudioV2Bleep && equalTo((AudioV2Bleep) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(AudioV2Bleep other) { + return startPadding.equals(other.startPadding) + && stopPadding.equals(other.stopPadding) + && frequency.equals(other.frequency) + && gain.equals(other.gain); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.startPadding, this.stopPadding, this.frequency, this.gain); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + private Optional startPadding = Optional.empty(); + + private Optional stopPadding = Optional.empty(); + + private Optional frequency = Optional.empty(); + + private Optional gain = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(AudioV2Bleep other) { + startPadding(other.getStartPadding()); + stopPadding(other.getStopPadding()); + frequency(other.getFrequency()); + gain(other.getGain()); + return this; + } + + /** + *

Padding added to the beginning of a bleep, in seconds. Range: 0–20. Defaults to 0.5.

+ */ + @JsonSetter(value = "startPadding", nulls = Nulls.SKIP) + public Builder startPadding(Optional startPadding) { + this.startPadding = startPadding; + return this; + } + + public Builder startPadding(Float startPadding) { + this.startPadding = Optional.ofNullable(startPadding); + return this; + } + + /** + *

Padding added to the end of a bleep, in seconds. Range: 0–20. Defaults to 0.2.

+ */ + @JsonSetter(value = "stopPadding", nulls = Nulls.SKIP) + public Builder stopPadding(Optional stopPadding) { + this.stopPadding = stopPadding; + return this; + } + + public Builder stopPadding(Float stopPadding) { + this.stopPadding = Optional.ofNullable(stopPadding); + return this; + } + + /** + *

Frequency of the sine wave used for the bleep sound, in Hz.

+ */ + @JsonSetter(value = "frequency", nulls = Nulls.SKIP) + public Builder frequency(Optional frequency) { + this.frequency = frequency; + return this; + } + + public Builder frequency(Integer frequency) { + this.frequency = Optional.ofNullable(frequency); + return this; + } + + /** + *

Relative loudness of the bleep. Positive values increase loudness and negative values decrease it.

+ */ + @JsonSetter(value = "gain", nulls = Nulls.SKIP) + public Builder gain(Optional gain) { + this.gain = gain; + return this; + } + + public Builder gain(Integer gain) { + this.gain = Optional.ofNullable(gain); + return this; + } + + public AudioV2Bleep build() { + return new AudioV2Bleep(startPadding, stopPadding, frequency, gain, additionalProperties); + } + + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/AudioV2OutputTranscription.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/AudioV2OutputTranscription.java new file mode 100644 index 00000000..9d48255f --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/AudioV2OutputTranscription.java @@ -0,0 +1,118 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +public final class AudioV2OutputTranscription { + public static final AudioV2OutputTranscription NONE = new AudioV2OutputTranscription(Value.NONE, "NONE"); + + public static final AudioV2OutputTranscription TRANSCRIPTION = + new AudioV2OutputTranscription(Value.TRANSCRIPTION, "TRANSCRIPTION"); + + public static final AudioV2OutputTranscription MEDICAL_TRANSCRIPTION = + new AudioV2OutputTranscription(Value.MEDICAL_TRANSCRIPTION, "MEDICAL_TRANSCRIPTION"); + + public static final AudioV2OutputTranscription DIARIZED_TRANSCRIPTION = + new AudioV2OutputTranscription(Value.DIARIZED_TRANSCRIPTION, "DIARIZED_TRANSCRIPTION"); + + public static final AudioV2OutputTranscription MEDICAL_DIARIZED_TRANSCRIPTION = + new AudioV2OutputTranscription(Value.MEDICAL_DIARIZED_TRANSCRIPTION, "MEDICAL_DIARIZED_TRANSCRIPTION"); + + private final Value value; + + private final String string; + + AudioV2OutputTranscription(Value value, String string) { + this.value = value; + this.string = string; + } + + public Value getEnumValue() { + return value; + } + + @java.lang.Override + @JsonValue + public String toString() { + return this.string; + } + + @java.lang.Override + public boolean equals(Object other) { + return (this == other) + || (other instanceof AudioV2OutputTranscription + && this.string.equals(((AudioV2OutputTranscription) other).string)); + } + + @java.lang.Override + public int hashCode() { + return this.string.hashCode(); + } + + public T visit(Visitor visitor) { + switch (value) { + case NONE: + return visitor.visitNone(); + case TRANSCRIPTION: + return visitor.visitTranscription(); + case MEDICAL_TRANSCRIPTION: + return visitor.visitMedicalTranscription(); + case DIARIZED_TRANSCRIPTION: + return visitor.visitDiarizedTranscription(); + case MEDICAL_DIARIZED_TRANSCRIPTION: + return visitor.visitMedicalDiarizedTranscription(); + case UNKNOWN: + default: + return visitor.visitUnknown(string); + } + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static AudioV2OutputTranscription valueOf(String value) { + switch (value) { + case "NONE": + return NONE; + case "TRANSCRIPTION": + return TRANSCRIPTION; + case "MEDICAL_TRANSCRIPTION": + return MEDICAL_TRANSCRIPTION; + case "DIARIZED_TRANSCRIPTION": + return DIARIZED_TRANSCRIPTION; + case "MEDICAL_DIARIZED_TRANSCRIPTION": + return MEDICAL_DIARIZED_TRANSCRIPTION; + default: + return new AudioV2OutputTranscription(Value.UNKNOWN, value); + } + } + + public enum Value { + NONE, + + TRANSCRIPTION, + + MEDICAL_TRANSCRIPTION, + + DIARIZED_TRANSCRIPTION, + + MEDICAL_DIARIZED_TRANSCRIPTION, + + UNKNOWN + } + + public interface Visitor { + T visitNone(); + + T visitTranscription(); + + T visitMedicalTranscription(); + + T visitDiarizedTranscription(); + + T visitMedicalDiarizedTranscription(); + + T visitUnknown(String unknownType); + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/DeidentifyFileResponseV2.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/DeidentifyFileResponseV2.java new file mode 100644 index 00000000..fa5b77eb --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/DeidentifyFileResponseV2.java @@ -0,0 +1,111 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = DeidentifyFileResponseV2.Builder.class) +public final class DeidentifyFileResponseV2 { + private final Optional runId; + + private final Map additionalProperties; + + private DeidentifyFileResponseV2(Optional runId, Map additionalProperties) { + this.runId = runId; + this.additionalProperties = additionalProperties; + } + + /** + * @return Run ID of the deidentification request. + */ + @JsonProperty("runId") + public Optional getRunId() { + return runId; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof DeidentifyFileResponseV2 && equalTo((DeidentifyFileResponseV2) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(DeidentifyFileResponseV2 other) { + return runId.equals(other.runId); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.runId); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + private Optional runId = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(DeidentifyFileResponseV2 other) { + runId(other.getRunId()); + return this; + } + + /** + *

Run ID of the deidentification request.

+ */ + @JsonSetter(value = "runId", nulls = Nulls.SKIP) + public Builder runId(Optional runId) { + this.runId = runId; + return this; + } + + public Builder runId(String runId) { + this.runId = Optional.ofNullable(runId); + return this; + } + + public DeidentifyFileResponseV2 build() { + return new DeidentifyFileResponseV2(runId, additionalProperties); + } + + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/DeidentifyStringResponseV2.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/DeidentifyStringResponseV2.java new file mode 100644 index 00000000..eb0eca2d --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/DeidentifyStringResponseV2.java @@ -0,0 +1,168 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = DeidentifyStringResponseV2.Builder.class) +public final class DeidentifyStringResponseV2 { + private final Optional processedText; + + private final Optional> entities; + + private final Optional metrics; + + private final Map additionalProperties; + + private DeidentifyStringResponseV2( + Optional processedText, + Optional> entities, + Optional metrics, + Map additionalProperties) { + this.processedText = processedText; + this.entities = entities; + this.metrics = metrics; + this.additionalProperties = additionalProperties; + } + + /** + * @return Deidentified text with sensitive entities replaced. + */ + @JsonProperty("processedText") + public Optional getProcessedText() { + return processedText; + } + + /** + * @return Detected entities. + */ + @JsonProperty("entities") + public Optional> getEntities() { + return entities; + } + + @JsonProperty("metrics") + public Optional getMetrics() { + return metrics; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof DeidentifyStringResponseV2 && equalTo((DeidentifyStringResponseV2) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(DeidentifyStringResponseV2 other) { + return processedText.equals(other.processedText) + && entities.equals(other.entities) + && metrics.equals(other.metrics); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.processedText, this.entities, this.metrics); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + private Optional processedText = Optional.empty(); + + private Optional> entities = Optional.empty(); + + private Optional metrics = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(DeidentifyStringResponseV2 other) { + processedText(other.getProcessedText()); + entities(other.getEntities()); + metrics(other.getMetrics()); + return this; + } + + /** + *

Deidentified text with sensitive entities replaced.

+ */ + @JsonSetter(value = "processedText", nulls = Nulls.SKIP) + public Builder processedText(Optional processedText) { + this.processedText = processedText; + return this; + } + + public Builder processedText(String processedText) { + this.processedText = Optional.ofNullable(processedText); + return this; + } + + /** + *

Detected entities.

+ */ + @JsonSetter(value = "entities", nulls = Nulls.SKIP) + public Builder entities(Optional> entities) { + this.entities = entities; + return this; + } + + public Builder entities(List entities) { + this.entities = Optional.ofNullable(entities); + return this; + } + + @JsonSetter(value = "metrics", nulls = Nulls.SKIP) + public Builder metrics(Optional metrics) { + this.metrics = metrics; + return this; + } + + public Builder metrics(Metrics metrics) { + this.metrics = Optional.ofNullable(metrics); + return this; + } + + public DeidentifyStringResponseV2 build() { + return new DeidentifyStringResponseV2(processedText, entities, metrics, additionalProperties); + } + + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/DeidentifyStringResponseV2DetectedEntity.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/DeidentifyStringResponseV2DetectedEntity.java new file mode 100644 index 00000000..e3bf15bd --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/DeidentifyStringResponseV2DetectedEntity.java @@ -0,0 +1,229 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = DeidentifyStringResponseV2DetectedEntity.Builder.class) +public final class DeidentifyStringResponseV2DetectedEntity { + private final Optional token; + + private final Optional value; + + private final Optional location; + + private final Optional entityType; + + private final Optional> entityScores; + + private final Map additionalProperties; + + private DeidentifyStringResponseV2DetectedEntity( + Optional token, + Optional value, + Optional location, + Optional entityType, + Optional> entityScores, + Map additionalProperties) { + this.token = token; + this.value = value; + this.location = location; + this.entityType = entityType; + this.entityScores = entityScores; + this.additionalProperties = additionalProperties; + } + + /** + * @return Processed text of the entity. + */ + @JsonProperty("token") + public Optional getToken() { + return token; + } + + /** + * @return Original text of the entity. + */ + @JsonProperty("value") + public Optional getValue() { + return value; + } + + @JsonProperty("location") + public Optional getLocation() { + return location; + } + + /** + * @return Highest-rated label. + */ + @JsonProperty("entityType") + public Optional getEntityType() { + return entityType; + } + + /** + * @return Labels and their scores. + */ + @JsonProperty("entityScores") + public Optional> getEntityScores() { + return entityScores; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof DeidentifyStringResponseV2DetectedEntity + && equalTo((DeidentifyStringResponseV2DetectedEntity) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(DeidentifyStringResponseV2DetectedEntity other) { + return token.equals(other.token) + && value.equals(other.value) + && location.equals(other.location) + && entityType.equals(other.entityType) + && entityScores.equals(other.entityScores); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.token, this.value, this.location, this.entityType, this.entityScores); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + private Optional token = Optional.empty(); + + private Optional value = Optional.empty(); + + private Optional location = Optional.empty(); + + private Optional entityType = Optional.empty(); + + private Optional> entityScores = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(DeidentifyStringResponseV2DetectedEntity other) { + token(other.getToken()); + value(other.getValue()); + location(other.getLocation()); + entityType(other.getEntityType()); + entityScores(other.getEntityScores()); + return this; + } + + /** + *

Processed text of the entity.

+ */ + @JsonSetter(value = "token", nulls = Nulls.SKIP) + public Builder token(Optional token) { + this.token = token; + return this; + } + + public Builder token(String token) { + this.token = Optional.ofNullable(token); + return this; + } + + /** + *

Original text of the entity.

+ */ + @JsonSetter(value = "value", nulls = Nulls.SKIP) + public Builder value(Optional value) { + this.value = value; + return this; + } + + public Builder value(String value) { + this.value = Optional.ofNullable(value); + return this; + } + + @JsonSetter(value = "location", nulls = Nulls.SKIP) + public Builder location(Optional location) { + this.location = location; + return this; + } + + public Builder location(DeidentifyStringResponseV2EntityLocation location) { + this.location = Optional.ofNullable(location); + return this; + } + + /** + *

Highest-rated label.

+ */ + @JsonSetter(value = "entityType", nulls = Nulls.SKIP) + public Builder entityType(Optional entityType) { + this.entityType = entityType; + return this; + } + + public Builder entityType(String entityType) { + this.entityType = Optional.ofNullable(entityType); + return this; + } + + /** + *

Labels and their scores.

+ */ + @JsonSetter(value = "entityScores", nulls = Nulls.SKIP) + public Builder entityScores(Optional> entityScores) { + this.entityScores = entityScores; + return this; + } + + public Builder entityScores(Map entityScores) { + this.entityScores = Optional.ofNullable(entityScores); + return this; + } + + public DeidentifyStringResponseV2DetectedEntity build() { + return new DeidentifyStringResponseV2DetectedEntity( + token, value, location, entityType, entityScores, additionalProperties); + } + + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/DeidentifyStringResponseV2EntityLocation.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/DeidentifyStringResponseV2EntityLocation.java new file mode 100644 index 00000000..6ac6a633 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/DeidentifyStringResponseV2EntityLocation.java @@ -0,0 +1,205 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = DeidentifyStringResponseV2EntityLocation.Builder.class) +public final class DeidentifyStringResponseV2EntityLocation { + private final Optional startIndex; + + private final Optional endIndex; + + private final Optional startIndexProcessed; + + private final Optional endIndexProcessed; + + private final Map additionalProperties; + + private DeidentifyStringResponseV2EntityLocation( + Optional startIndex, + Optional endIndex, + Optional startIndexProcessed, + Optional endIndexProcessed, + Map additionalProperties) { + this.startIndex = startIndex; + this.endIndex = endIndex; + this.startIndexProcessed = startIndexProcessed; + this.endIndexProcessed = endIndexProcessed; + this.additionalProperties = additionalProperties; + } + + /** + * @return Index of the first character of the string in the original text. + */ + @JsonProperty("startIndex") + public Optional getStartIndex() { + return startIndex; + } + + /** + * @return Index of the last character of the string in the original text. + */ + @JsonProperty("endIndex") + public Optional getEndIndex() { + return endIndex; + } + + /** + * @return Index of the first character of the string in the processed text. + */ + @JsonProperty("startIndexProcessed") + public Optional getStartIndexProcessed() { + return startIndexProcessed; + } + + /** + * @return Index of the last character of the string in the processed text. + */ + @JsonProperty("endIndexProcessed") + public Optional getEndIndexProcessed() { + return endIndexProcessed; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof DeidentifyStringResponseV2EntityLocation + && equalTo((DeidentifyStringResponseV2EntityLocation) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(DeidentifyStringResponseV2EntityLocation other) { + return startIndex.equals(other.startIndex) + && endIndex.equals(other.endIndex) + && startIndexProcessed.equals(other.startIndexProcessed) + && endIndexProcessed.equals(other.endIndexProcessed); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.startIndex, this.endIndex, this.startIndexProcessed, this.endIndexProcessed); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + private Optional startIndex = Optional.empty(); + + private Optional endIndex = Optional.empty(); + + private Optional startIndexProcessed = Optional.empty(); + + private Optional endIndexProcessed = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(DeidentifyStringResponseV2EntityLocation other) { + startIndex(other.getStartIndex()); + endIndex(other.getEndIndex()); + startIndexProcessed(other.getStartIndexProcessed()); + endIndexProcessed(other.getEndIndexProcessed()); + return this; + } + + /** + *

Index of the first character of the string in the original text.

+ */ + @JsonSetter(value = "startIndex", nulls = Nulls.SKIP) + public Builder startIndex(Optional startIndex) { + this.startIndex = startIndex; + return this; + } + + public Builder startIndex(Integer startIndex) { + this.startIndex = Optional.ofNullable(startIndex); + return this; + } + + /** + *

Index of the last character of the string in the original text.

+ */ + @JsonSetter(value = "endIndex", nulls = Nulls.SKIP) + public Builder endIndex(Optional endIndex) { + this.endIndex = endIndex; + return this; + } + + public Builder endIndex(Integer endIndex) { + this.endIndex = Optional.ofNullable(endIndex); + return this; + } + + /** + *

Index of the first character of the string in the processed text.

+ */ + @JsonSetter(value = "startIndexProcessed", nulls = Nulls.SKIP) + public Builder startIndexProcessed(Optional startIndexProcessed) { + this.startIndexProcessed = startIndexProcessed; + return this; + } + + public Builder startIndexProcessed(Integer startIndexProcessed) { + this.startIndexProcessed = Optional.ofNullable(startIndexProcessed); + return this; + } + + /** + *

Index of the last character of the string in the processed text.

+ */ + @JsonSetter(value = "endIndexProcessed", nulls = Nulls.SKIP) + public Builder endIndexProcessed(Optional endIndexProcessed) { + this.endIndexProcessed = endIndexProcessed; + return this; + } + + public Builder endIndexProcessed(Integer endIndexProcessed) { + this.endIndexProcessed = Optional.ofNullable(endIndexProcessed); + return this; + } + + public DeidentifyStringResponseV2EntityLocation build() { + return new DeidentifyStringResponseV2EntityLocation( + startIndex, endIndex, startIndexProcessed, endIndexProcessed, additionalProperties); + } + + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/Detect.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/Detect.java new file mode 100644 index 00000000..4935b1d0 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/Detect.java @@ -0,0 +1,266 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = Detect.Builder.class) +public final class Detect { + private final Optional> entities; + + private final Optional> objectEntities; + + private final Optional> restrict; + + private final Optional> skip; + + private final Optional> customTypes; + + private final Optional returnEntities; + + private final Map additionalProperties; + + private Detect( + Optional> entities, + Optional> objectEntities, + Optional> restrict, + Optional> skip, + Optional> customTypes, + Optional returnEntities, + Map additionalProperties) { + this.entities = entities; + this.objectEntities = objectEntities; + this.restrict = restrict; + this.skip = skip; + this.customTypes = customTypes; + this.returnEntities = returnEntities; + this.additionalProperties = additionalProperties; + } + + /** + * @return Entities and their deidentification settings. + */ + @JsonProperty("entities") + public Optional> getEntities() { + return entities; + } + + /** + * @return Object entities and their deidentification settings. + */ + @JsonProperty("objectEntities") + public Optional> getObjectEntities() { + return objectEntities; + } + + /** + * @return Settings for restricting detected entities. A detected entity is replaced with '[RESTRICTED]' only when a restrict pattern matches the entity's full value. If a pattern matches only a substring of the detected entity, the pattern is ignored and the entity is processed according to the configured tokenization and transformation settings. Patterns don't match across entity boundaries. If an entity matches both a skip and a restrict pattern, skip takes precedence and the entity is displayed in plaintext. Note: Vault tokens are currently not supported. + */ + @JsonProperty("restrict") + public Optional> getRestrict() { + return restrict; + } + + /** + * @return Settings for bypassing deidentification on detected entities. A detected entity is displayed in plaintext when a skip pattern matches either the entity's full value or any substring of it. Patterns don't match across entity boundaries. If an entity matches both a skip and a restrict pattern, skip takes precedence and the entity is displayed in plaintext. + */ + @JsonProperty("skip") + public Optional> getSkip() { + return skip; + } + + /** + * @return Custom entity type definitions. + */ + @JsonProperty("customTypes") + public Optional> getCustomTypes() { + return customTypes; + } + + /** + * @return Controls which detected entities are returned in the response. EXCLUDE_SENSITIVE_DATA: returns each detected entity's metadata (token, entity type, location, and confidence scores) but omits the original sensitive value. ALL: returns each detected entity's metadata and the original sensitive value. NONE: doesn't return detected entities. + */ + @JsonProperty("returnEntities") + public Optional getReturnEntities() { + return returnEntities; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof Detect && equalTo((Detect) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(Detect other) { + return entities.equals(other.entities) + && objectEntities.equals(other.objectEntities) + && restrict.equals(other.restrict) + && skip.equals(other.skip) + && customTypes.equals(other.customTypes) + && returnEntities.equals(other.returnEntities); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash( + this.entities, this.objectEntities, this.restrict, this.skip, this.customTypes, this.returnEntities); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + private Optional> entities = Optional.empty(); + + private Optional> objectEntities = Optional.empty(); + + private Optional> restrict = Optional.empty(); + + private Optional> skip = Optional.empty(); + + private Optional> customTypes = Optional.empty(); + + private Optional returnEntities = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(Detect other) { + entities(other.getEntities()); + objectEntities(other.getObjectEntities()); + restrict(other.getRestrict()); + skip(other.getSkip()); + customTypes(other.getCustomTypes()); + returnEntities(other.getReturnEntities()); + return this; + } + + /** + *

Entities and their deidentification settings.

+ */ + @JsonSetter(value = "entities", nulls = Nulls.SKIP) + public Builder entities(Optional> entities) { + this.entities = entities; + return this; + } + + public Builder entities(List entities) { + this.entities = Optional.ofNullable(entities); + return this; + } + + /** + *

Object entities and their deidentification settings.

+ */ + @JsonSetter(value = "objectEntities", nulls = Nulls.SKIP) + public Builder objectEntities(Optional> objectEntities) { + this.objectEntities = objectEntities; + return this; + } + + public Builder objectEntities(List objectEntities) { + this.objectEntities = Optional.ofNullable(objectEntities); + return this; + } + + /** + *

Settings for restricting detected entities. A detected entity is replaced with '[RESTRICTED]' only when a restrict pattern matches the entity's full value. If a pattern matches only a substring of the detected entity, the pattern is ignored and the entity is processed according to the configured tokenization and transformation settings. Patterns don't match across entity boundaries. If an entity matches both a skip and a restrict pattern, skip takes precedence and the entity is displayed in plaintext. Note: Vault tokens are currently not supported.

+ */ + @JsonSetter(value = "restrict", nulls = Nulls.SKIP) + public Builder restrict(Optional> restrict) { + this.restrict = restrict; + return this; + } + + public Builder restrict(List restrict) { + this.restrict = Optional.ofNullable(restrict); + return this; + } + + /** + *

Settings for bypassing deidentification on detected entities. A detected entity is displayed in plaintext when a skip pattern matches either the entity's full value or any substring of it. Patterns don't match across entity boundaries. If an entity matches both a skip and a restrict pattern, skip takes precedence and the entity is displayed in plaintext.

+ */ + @JsonSetter(value = "skip", nulls = Nulls.SKIP) + public Builder skip(Optional> skip) { + this.skip = skip; + return this; + } + + public Builder skip(List skip) { + this.skip = Optional.ofNullable(skip); + return this; + } + + /** + *

Custom entity type definitions.

+ */ + @JsonSetter(value = "customTypes", nulls = Nulls.SKIP) + public Builder customTypes(Optional> customTypes) { + this.customTypes = customTypes; + return this; + } + + public Builder customTypes(List customTypes) { + this.customTypes = Optional.ofNullable(customTypes); + return this; + } + + /** + *

Controls which detected entities are returned in the response. EXCLUDE_SENSITIVE_DATA: returns each detected entity's metadata (token, entity type, location, and confidence scores) but omits the original sensitive value. ALL: returns each detected entity's metadata and the original sensitive value. NONE: doesn't return detected entities.

+ */ + @JsonSetter(value = "returnEntities", nulls = Nulls.SKIP) + public Builder returnEntities(Optional returnEntities) { + this.returnEntities = returnEntities; + return this; + } + + public Builder returnEntities(DetectReturnEntities returnEntities) { + this.returnEntities = Optional.ofNullable(returnEntities); + return this; + } + + public Detect build() { + return new Detect( + entities, objectEntities, restrict, skip, customTypes, returnEntities, additionalProperties); + } + + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectConfigV2.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectConfigV2.java new file mode 100644 index 00000000..77f00c81 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectConfigV2.java @@ -0,0 +1,470 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import org.jetbrains.annotations.NotNull; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = DetectConfigV2.Builder.class) +public final class DetectConfigV2 { + private final Optional id; + + private final Optional name; + + private final Optional namespace; + + private final Optional description; + + private final String vaultId; + + private final Optional vaultType; + + private final Optional detect; + + private final Optional> fileMapping; + + private final Optional media; + + private final Map additionalProperties; + + private DetectConfigV2( + Optional id, + Optional name, + Optional namespace, + Optional description, + String vaultId, + Optional vaultType, + Optional detect, + Optional> fileMapping, + Optional media, + Map additionalProperties) { + this.id = id; + this.name = name; + this.namespace = namespace; + this.description = description; + this.vaultId = vaultId; + this.vaultType = vaultType; + this.detect = detect; + this.fileMapping = fileMapping; + this.media = media; + this.additionalProperties = additionalProperties; + } + + /** + * @return ID of the configuration. + */ + @JsonProperty("ID") + public Optional getId() { + return id; + } + + /** + * @return Name of the configuration. + */ + @JsonProperty("name") + public Optional getName() { + return name; + } + + /** + * @return Namespace of the configuration. + */ + @JsonProperty("namespace") + public Optional getNamespace() { + return namespace; + } + + /** + * @return Description of the configuration. + */ + @JsonProperty("description") + public Optional getDescription() { + return description; + } + + /** + * @return ID of the vault. + */ + @JsonProperty("vaultId") + public String getVaultId() { + return vaultId; + } + + /** + * @return Type of the vault. + */ + @JsonProperty("vaultType") + public Optional getVaultType() { + return vaultType; + } + + @JsonProperty("detect") + public Optional getDetect() { + return detect; + } + + /** + * @return Mappings for source and deidentified file locations. + */ + @JsonProperty("fileMapping") + public Optional> getFileMapping() { + return fileMapping; + } + + @JsonProperty("media") + public Optional getMedia() { + return media; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof DetectConfigV2 && equalTo((DetectConfigV2) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(DetectConfigV2 other) { + return id.equals(other.id) + && name.equals(other.name) + && namespace.equals(other.namespace) + && description.equals(other.description) + && vaultId.equals(other.vaultId) + && vaultType.equals(other.vaultType) + && detect.equals(other.detect) + && fileMapping.equals(other.fileMapping) + && media.equals(other.media); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash( + this.id, + this.name, + this.namespace, + this.description, + this.vaultId, + this.vaultType, + this.detect, + this.fileMapping, + this.media); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static VaultIdStage builder() { + return new Builder(); + } + + public interface VaultIdStage { + /** + *

ID of the vault.

+ */ + _FinalStage vaultId(@NotNull String vaultId); + + Builder from(DetectConfigV2 other); + } + + public interface _FinalStage { + DetectConfigV2 build(); + + _FinalStage additionalProperty(String key, Object value); + + _FinalStage additionalProperties(Map additionalProperties); + + /** + *

ID of the configuration.

+ */ + _FinalStage id(Optional id); + + _FinalStage id(String id); + + /** + *

Name of the configuration.

+ */ + _FinalStage name(Optional name); + + _FinalStage name(String name); + + /** + *

Namespace of the configuration.

+ */ + _FinalStage namespace(Optional namespace); + + _FinalStage namespace(String namespace); + + /** + *

Description of the configuration.

+ */ + _FinalStage description(Optional description); + + _FinalStage description(String description); + + /** + *

Type of the vault.

+ */ + _FinalStage vaultType(Optional vaultType); + + _FinalStage vaultType(DetectConfigV2VaultType vaultType); + + _FinalStage detect(Optional detect); + + _FinalStage detect(Detect detect); + + /** + *

Mappings for source and deidentified file locations.

+ */ + _FinalStage fileMapping(Optional> fileMapping); + + _FinalStage fileMapping(List fileMapping); + + _FinalStage media(Optional media); + + _FinalStage media(Media media); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder implements VaultIdStage, _FinalStage { + private String vaultId; + + private Optional media = Optional.empty(); + + private Optional> fileMapping = Optional.empty(); + + private Optional detect = Optional.empty(); + + private Optional vaultType = Optional.empty(); + + private Optional description = Optional.empty(); + + private Optional namespace = Optional.empty(); + + private Optional name = Optional.empty(); + + private Optional id = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + @java.lang.Override + public Builder from(DetectConfigV2 other) { + id(other.getId()); + name(other.getName()); + namespace(other.getNamespace()); + description(other.getDescription()); + vaultId(other.getVaultId()); + vaultType(other.getVaultType()); + detect(other.getDetect()); + fileMapping(other.getFileMapping()); + media(other.getMedia()); + return this; + } + + /** + *

ID of the vault.

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonSetter("vaultId") + public _FinalStage vaultId(@NotNull String vaultId) { + this.vaultId = Objects.requireNonNull(vaultId, "vaultId must not be null"); + return this; + } + + @java.lang.Override + public _FinalStage media(Media media) { + this.media = Optional.ofNullable(media); + return this; + } + + @java.lang.Override + @JsonSetter(value = "media", nulls = Nulls.SKIP) + public _FinalStage media(Optional media) { + this.media = media; + return this; + } + + /** + *

Mappings for source and deidentified file locations.

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + public _FinalStage fileMapping(List fileMapping) { + this.fileMapping = Optional.ofNullable(fileMapping); + return this; + } + + /** + *

Mappings for source and deidentified file locations.

+ */ + @java.lang.Override + @JsonSetter(value = "fileMapping", nulls = Nulls.SKIP) + public _FinalStage fileMapping(Optional> fileMapping) { + this.fileMapping = fileMapping; + return this; + } + + @java.lang.Override + public _FinalStage detect(Detect detect) { + this.detect = Optional.ofNullable(detect); + return this; + } + + @java.lang.Override + @JsonSetter(value = "detect", nulls = Nulls.SKIP) + public _FinalStage detect(Optional detect) { + this.detect = detect; + return this; + } + + /** + *

Type of the vault.

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + public _FinalStage vaultType(DetectConfigV2VaultType vaultType) { + this.vaultType = Optional.ofNullable(vaultType); + return this; + } + + /** + *

Type of the vault.

+ */ + @java.lang.Override + @JsonSetter(value = "vaultType", nulls = Nulls.SKIP) + public _FinalStage vaultType(Optional vaultType) { + this.vaultType = vaultType; + return this; + } + + /** + *

Description of the configuration.

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + public _FinalStage description(String description) { + this.description = Optional.ofNullable(description); + return this; + } + + /** + *

Description of the configuration.

+ */ + @java.lang.Override + @JsonSetter(value = "description", nulls = Nulls.SKIP) + public _FinalStage description(Optional description) { + this.description = description; + return this; + } + + /** + *

Namespace of the configuration.

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + public _FinalStage namespace(String namespace) { + this.namespace = Optional.ofNullable(namespace); + return this; + } + + /** + *

Namespace of the configuration.

+ */ + @java.lang.Override + @JsonSetter(value = "namespace", nulls = Nulls.SKIP) + public _FinalStage namespace(Optional namespace) { + this.namespace = namespace; + return this; + } + + /** + *

Name of the configuration.

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + public _FinalStage name(String name) { + this.name = Optional.ofNullable(name); + return this; + } + + /** + *

Name of the configuration.

+ */ + @java.lang.Override + @JsonSetter(value = "name", nulls = Nulls.SKIP) + public _FinalStage name(Optional name) { + this.name = name; + return this; + } + + /** + *

ID of the configuration.

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + public _FinalStage id(String id) { + this.id = Optional.ofNullable(id); + return this; + } + + /** + *

ID of the configuration.

+ */ + @java.lang.Override + @JsonSetter(value = "ID", nulls = Nulls.SKIP) + public _FinalStage id(Optional id) { + this.id = id; + return this; + } + + @java.lang.Override + public DetectConfigV2 build() { + return new DetectConfigV2( + id, + name, + namespace, + description, + vaultId, + vaultType, + detect, + fileMapping, + media, + additionalProperties); + } + + @java.lang.Override + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + @java.lang.Override + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectConfigV2VaultType.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectConfigV2VaultType.java new file mode 100644 index 00000000..4249b6a3 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectConfigV2VaultType.java @@ -0,0 +1,105 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +public final class DetectConfigV2VaultType { + public static final DetectConfigV2VaultType PRIVACYDB = new DetectConfigV2VaultType(Value.PRIVACYDB, "PRIVACYDB"); + + public static final DetectConfigV2VaultType FLOWDB = new DetectConfigV2VaultType(Value.FLOWDB, "FLOWDB"); + + public static final DetectConfigV2VaultType NONE = new DetectConfigV2VaultType(Value.NONE, "NONE"); + + public static final DetectConfigV2VaultType FLOWDB_SCHEMALESS = + new DetectConfigV2VaultType(Value.FLOWDB_SCHEMALESS, "FLOWDB_SCHEMALESS"); + + private final Value value; + + private final String string; + + DetectConfigV2VaultType(Value value, String string) { + this.value = value; + this.string = string; + } + + public Value getEnumValue() { + return value; + } + + @java.lang.Override + @JsonValue + public String toString() { + return this.string; + } + + @java.lang.Override + public boolean equals(Object other) { + return (this == other) + || (other instanceof DetectConfigV2VaultType + && this.string.equals(((DetectConfigV2VaultType) other).string)); + } + + @java.lang.Override + public int hashCode() { + return this.string.hashCode(); + } + + public T visit(Visitor visitor) { + switch (value) { + case PRIVACYDB: + return visitor.visitPrivacydb(); + case FLOWDB: + return visitor.visitFlowdb(); + case NONE: + return visitor.visitNone(); + case FLOWDB_SCHEMALESS: + return visitor.visitFlowdbSchemaless(); + case UNKNOWN: + default: + return visitor.visitUnknown(string); + } + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static DetectConfigV2VaultType valueOf(String value) { + switch (value) { + case "PRIVACYDB": + return PRIVACYDB; + case "FLOWDB": + return FLOWDB; + case "NONE": + return NONE; + case "FLOWDB_SCHEMALESS": + return FLOWDB_SCHEMALESS; + default: + return new DetectConfigV2VaultType(Value.UNKNOWN, value); + } + } + + public enum Value { + NONE, + + PRIVACYDB, + + FLOWDB, + + FLOWDB_SCHEMALESS, + + UNKNOWN + } + + public interface Visitor { + T visitNone(); + + T visitPrivacydb(); + + T visitFlowdb(); + + T visitFlowdbSchemaless(); + + T visitUnknown(String unknownType); + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectCustomTypes.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectCustomTypes.java new file mode 100644 index 00000000..a4db8e7c --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectCustomTypes.java @@ -0,0 +1,141 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = DetectCustomTypes.Builder.class) +public final class DetectCustomTypes { + private final Optional label; + + private final Optional> match; + + private final Map additionalProperties; + + private DetectCustomTypes( + Optional label, Optional> match, Map additionalProperties) { + this.label = label; + this.match = match; + this.additionalProperties = additionalProperties; + } + + /** + * @return Label of the custom type. Note: Vault tokens are currently not supported. + */ + @JsonProperty("label") + public Optional getLabel() { + return label; + } + + /** + * @return Regular expressions to replace with '[<customType.label>]' enclosed in square brackets. Expressions must match the entirety of a detected entity, not just a substring, for the entity to be redacted. Expressions don't match across entity boundaries. If a string or entity matches both skip and customType.label, the entity is displayed in plaintext. If a string is detected as an entity and a customType.match pattern matches the entire detected entity, the entity is replaced with '[<customType.label>]'. If a string is detected as an entity but a customType.match pattern only matches a substring of it, the customType.match pattern is ignored, and the entity is processed according to the specified tokenization and transformation settings. + */ + @JsonProperty("match") + public Optional> getMatch() { + return match; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof DetectCustomTypes && equalTo((DetectCustomTypes) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(DetectCustomTypes other) { + return label.equals(other.label) && match.equals(other.match); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.label, this.match); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + private Optional label = Optional.empty(); + + private Optional> match = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(DetectCustomTypes other) { + label(other.getLabel()); + match(other.getMatch()); + return this; + } + + /** + *

Label of the custom type. Note: Vault tokens are currently not supported.

+ */ + @JsonSetter(value = "label", nulls = Nulls.SKIP) + public Builder label(Optional label) { + this.label = label; + return this; + } + + public Builder label(String label) { + this.label = Optional.ofNullable(label); + return this; + } + + /** + *

Regular expressions to replace with '[<customType.label>]' enclosed in square brackets. Expressions must match the entirety of a detected entity, not just a substring, for the entity to be redacted. Expressions don't match across entity boundaries. If a string or entity matches both skip and customType.label, the entity is displayed in plaintext. If a string is detected as an entity and a customType.match pattern matches the entire detected entity, the entity is replaced with '[<customType.label>]'. If a string is detected as an entity but a customType.match pattern only matches a substring of it, the customType.match pattern is ignored, and the entity is processed according to the specified tokenization and transformation settings.

+ */ + @JsonSetter(value = "match", nulls = Nulls.SKIP) + public Builder match(Optional> match) { + this.match = match; + return this; + } + + public Builder match(List match) { + this.match = Optional.ofNullable(match); + return this; + } + + public DetectCustomTypes build() { + return new DetectCustomTypes(label, match, additionalProperties); + } + + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectEntity.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectEntity.java new file mode 100644 index 00000000..472e7044 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectEntity.java @@ -0,0 +1,198 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = DetectEntity.Builder.class) +public final class DetectEntity { + private final Optional entityType; + + private final Optional deidentificationType; + + private final Optional destination; + + private final Optional transformation; + + private final Map additionalProperties; + + private DetectEntity( + Optional entityType, + Optional deidentificationType, + Optional destination, + Optional transformation, + Map additionalProperties) { + this.entityType = entityType; + this.deidentificationType = deidentificationType; + this.destination = destination; + this.transformation = transformation; + this.additionalProperties = additionalProperties; + } + + /** + * @return Type of entity to detect. + */ + @JsonProperty("entityType") + public Optional getEntityType() { + return entityType; + } + + /** + * @return Type of deidentification to apply. VAULT_TOKEN is only supported for the following file formats: txt, json, csv, xml, jsonl, docx, xlsx, pptx. + */ + @JsonProperty("deidentificationType") + public Optional getDeidentificationType() { + return deidentificationType; + } + + /** + * @return Vault destination where detected values of this entity type are stored. For vaults with a schema, specify the table and column in table.column format (for example, persons.name). For schemaless vaults, specify the token group name (for example, personTokenGroup). + */ + @JsonProperty("destination") + public Optional getDestination() { + return destination; + } + + @JsonProperty("transformation") + public Optional getTransformation() { + return transformation; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof DetectEntity && equalTo((DetectEntity) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(DetectEntity other) { + return entityType.equals(other.entityType) + && deidentificationType.equals(other.deidentificationType) + && destination.equals(other.destination) + && transformation.equals(other.transformation); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.entityType, this.deidentificationType, this.destination, this.transformation); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + private Optional entityType = Optional.empty(); + + private Optional deidentificationType = Optional.empty(); + + private Optional destination = Optional.empty(); + + private Optional transformation = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(DetectEntity other) { + entityType(other.getEntityType()); + deidentificationType(other.getDeidentificationType()); + destination(other.getDestination()); + transformation(other.getTransformation()); + return this; + } + + /** + *

Type of entity to detect.

+ */ + @JsonSetter(value = "entityType", nulls = Nulls.SKIP) + public Builder entityType(Optional entityType) { + this.entityType = entityType; + return this; + } + + public Builder entityType(DetectEntityEntityType entityType) { + this.entityType = Optional.ofNullable(entityType); + return this; + } + + /** + *

Type of deidentification to apply. VAULT_TOKEN is only supported for the following file formats: txt, json, csv, xml, jsonl, docx, xlsx, pptx.

+ */ + @JsonSetter(value = "deidentificationType", nulls = Nulls.SKIP) + public Builder deidentificationType(Optional deidentificationType) { + this.deidentificationType = deidentificationType; + return this; + } + + public Builder deidentificationType(DetectEntityDeidentificationType deidentificationType) { + this.deidentificationType = Optional.ofNullable(deidentificationType); + return this; + } + + /** + *

Vault destination where detected values of this entity type are stored. For vaults with a schema, specify the table and column in table.column format (for example, persons.name). For schemaless vaults, specify the token group name (for example, personTokenGroup).

+ */ + @JsonSetter(value = "destination", nulls = Nulls.SKIP) + public Builder destination(Optional destination) { + this.destination = destination; + return this; + } + + public Builder destination(String destination) { + this.destination = Optional.ofNullable(destination); + return this; + } + + @JsonSetter(value = "transformation", nulls = Nulls.SKIP) + public Builder transformation(Optional transformation) { + this.transformation = transformation; + return this; + } + + public Builder transformation(EntityTransformation transformation) { + this.transformation = Optional.ofNullable(transformation); + return this; + } + + public DetectEntity build() { + return new DetectEntity( + entityType, deidentificationType, destination, transformation, additionalProperties); + } + + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectEntityDeidentificationType.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectEntityDeidentificationType.java new file mode 100644 index 00000000..26262dce --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectEntityDeidentificationType.java @@ -0,0 +1,119 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +public final class DetectEntityDeidentificationType { + public static final DetectEntityDeidentificationType MASK = + new DetectEntityDeidentificationType(Value.MASK, "MASK"); + + public static final DetectEntityDeidentificationType ENTITY_ONLY = + new DetectEntityDeidentificationType(Value.ENTITY_ONLY, "ENTITY_ONLY"); + + public static final DetectEntityDeidentificationType VAULT_TOKEN = + new DetectEntityDeidentificationType(Value.VAULT_TOKEN, "VAULT_TOKEN"); + + public static final DetectEntityDeidentificationType UNKNOWN = + new DetectEntityDeidentificationType(Value.UNKNOWN, "UNKNOWN"); + + public static final DetectEntityDeidentificationType ENTITY_UNIQUE_COUNTER = + new DetectEntityDeidentificationType(Value.ENTITY_UNIQUE_COUNTER, "ENTITY_UNIQUE_COUNTER"); + + private final Value value; + + private final String string; + + DetectEntityDeidentificationType(Value value, String string) { + this.value = value; + this.string = string; + } + + public Value getEnumValue() { + return value; + } + + @java.lang.Override + @JsonValue + public String toString() { + return this.string; + } + + @java.lang.Override + public boolean equals(Object other) { + return (this == other) + || (other instanceof DetectEntityDeidentificationType + && this.string.equals(((DetectEntityDeidentificationType) other).string)); + } + + @java.lang.Override + public int hashCode() { + return this.string.hashCode(); + } + + public T visit(Visitor visitor) { + switch (value) { + case MASK: + return visitor.visitMask(); + case ENTITY_ONLY: + return visitor.visitEntityOnly(); + case VAULT_TOKEN: + return visitor.visitVaultToken(); + case UNKNOWN: + return visitor.visitUnknown(); + case ENTITY_UNIQUE_COUNTER: + return visitor.visitEntityUniqueCounter(); + case _UNKNOWN: + default: + return visitor.visitUnknown(string); + } + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static DetectEntityDeidentificationType valueOf(String value) { + switch (value) { + case "MASK": + return MASK; + case "ENTITY_ONLY": + return ENTITY_ONLY; + case "VAULT_TOKEN": + return VAULT_TOKEN; + case "UNKNOWN": + return UNKNOWN; + case "ENTITY_UNIQUE_COUNTER": + return ENTITY_UNIQUE_COUNTER; + default: + return new DetectEntityDeidentificationType(Value._UNKNOWN, value); + } + } + + public enum Value { + UNKNOWN, + + ENTITY_UNIQUE_COUNTER, + + ENTITY_ONLY, + + VAULT_TOKEN, + + MASK, + + _UNKNOWN + } + + public interface Visitor { + T visitUnknown(); + + T visitEntityUniqueCounter(); + + T visitEntityOnly(); + + T visitVaultToken(); + + T visitMask(); + + T visitUnknown(String unknownType); + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectEntityEntityType.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectEntityEntityType.java new file mode 100644 index 00000000..7e286cfc --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectEntityEntityType.java @@ -0,0 +1,806 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +public final class DetectEntityEntityType { + public static final DetectEntityEntityType PHONE_NUMBER = + new DetectEntityEntityType(Value.PHONE_NUMBER, "PHONE_NUMBER"); + + public static final DetectEntityEntityType USERNAME = new DetectEntityEntityType(Value.USERNAME, "USERNAME"); + + public static final DetectEntityEntityType VEHICLE_ID = new DetectEntityEntityType(Value.VEHICLE_ID, "VEHICLE_ID"); + + public static final DetectEntityEntityType RELIGION = new DetectEntityEntityType(Value.RELIGION, "RELIGION"); + + public static final DetectEntityEntityType ORIGIN = new DetectEntityEntityType(Value.ORIGIN, "ORIGIN"); + + public static final DetectEntityEntityType PASSPORT_NUMBER = + new DetectEntityEntityType(Value.PASSPORT_NUMBER, "PASSPORT_NUMBER"); + + public static final DetectEntityEntityType YEAR = new DetectEntityEntityType(Value.YEAR, "YEAR"); + + public static final DetectEntityEntityType CONDITION = new DetectEntityEntityType(Value.CONDITION, "CONDITION"); + + public static final DetectEntityEntityType LOCATION = new DetectEntityEntityType(Value.LOCATION, "LOCATION"); + + public static final DetectEntityEntityType LOCATION_CITY = + new DetectEntityEntityType(Value.LOCATION_CITY, "LOCATION_CITY"); + + public static final DetectEntityEntityType LOCATION_COUNTRY = + new DetectEntityEntityType(Value.LOCATION_COUNTRY, "LOCATION_COUNTRY"); + + public static final DetectEntityEntityType PROJECT = new DetectEntityEntityType(Value.PROJECT, "PROJECT"); + + public static final DetectEntityEntityType LOCATION_ZIP = + new DetectEntityEntityType(Value.LOCATION_ZIP, "LOCATION_ZIP"); + + public static final DetectEntityEntityType NAME_MEDICAL_PROFESSIONAL = + new DetectEntityEntityType(Value.NAME_MEDICAL_PROFESSIONAL, "NAME_MEDICAL_PROFESSIONAL"); + + public static final DetectEntityEntityType BANK_ACCOUNT = + new DetectEntityEntityType(Value.BANK_ACCOUNT, "BANK_ACCOUNT"); + + public static final DetectEntityEntityType OCCUPATION = new DetectEntityEntityType(Value.OCCUPATION, "OCCUPATION"); + + public static final DetectEntityEntityType TIME = new DetectEntityEntityType(Value.TIME, "TIME"); + + public static final DetectEntityEntityType LOCATION_STATE = + new DetectEntityEntityType(Value.LOCATION_STATE, "LOCATION_STATE"); + + public static final DetectEntityEntityType EVENT = new DetectEntityEntityType(Value.EVENT, "EVENT"); + + public static final DetectEntityEntityType POLITICAL_AFFILIATION = + new DetectEntityEntityType(Value.POLITICAL_AFFILIATION, "POLITICAL_AFFILIATION"); + + public static final DetectEntityEntityType LANGUAGE = new DetectEntityEntityType(Value.LANGUAGE, "LANGUAGE"); + + public static final DetectEntityEntityType ZODIAC_SIGN = + new DetectEntityEntityType(Value.ZODIAC_SIGN, "ZODIAC_SIGN"); + + public static final DetectEntityEntityType PRODUCT = new DetectEntityEntityType(Value.PRODUCT, "PRODUCT"); + + public static final DetectEntityEntityType SEXUALITY = new DetectEntityEntityType(Value.SEXUALITY, "SEXUALITY"); + + public static final DetectEntityEntityType DURATION = new DetectEntityEntityType(Value.DURATION, "DURATION"); + + public static final DetectEntityEntityType PHYSICAL_ATTRIBUTE = + new DetectEntityEntityType(Value.PHYSICAL_ATTRIBUTE, "PHYSICAL_ATTRIBUTE"); + + public static final DetectEntityEntityType LOCATION_ADDRESS_STREET = + new DetectEntityEntityType(Value.LOCATION_ADDRESS_STREET, "LOCATION_ADDRESS_STREET"); + + public static final DetectEntityEntityType MEDICAL_PROCESS = + new DetectEntityEntityType(Value.MEDICAL_PROCESS, "MEDICAL_PROCESS"); + + public static final DetectEntityEntityType LOCATION_ADDRESS = + new DetectEntityEntityType(Value.LOCATION_ADDRESS, "LOCATION_ADDRESS"); + + public static final DetectEntityEntityType FILENAME = new DetectEntityEntityType(Value.FILENAME, "FILENAME"); + + public static final DetectEntityEntityType DOB = new DetectEntityEntityType(Value.DOB, "DOB"); + + public static final DetectEntityEntityType ORGANIZATION_MEDICAL_FACILITY = + new DetectEntityEntityType(Value.ORGANIZATION_MEDICAL_FACILITY, "ORGANIZATION_MEDICAL_FACILITY"); + + public static final DetectEntityEntityType ROUTING_NUMBER = + new DetectEntityEntityType(Value.ROUTING_NUMBER, "ROUTING_NUMBER"); + + public static final DetectEntityEntityType HEALTHCARE_NUMBER = + new DetectEntityEntityType(Value.HEALTHCARE_NUMBER, "HEALTHCARE_NUMBER"); + + public static final DetectEntityEntityType GENDER = new DetectEntityEntityType(Value.GENDER, "GENDER"); + + public static final DetectEntityEntityType NAME = new DetectEntityEntityType(Value.NAME, "NAME"); + + public static final DetectEntityEntityType DRUG = new DetectEntityEntityType(Value.DRUG, "DRUG"); + + public static final DetectEntityEntityType NONE = new DetectEntityEntityType(Value.NONE, "NONE"); + + public static final DetectEntityEntityType DATE_INTERVAL = + new DetectEntityEntityType(Value.DATE_INTERVAL, "DATE_INTERVAL"); + + public static final DetectEntityEntityType MARITAL_STATUS = + new DetectEntityEntityType(Value.MARITAL_STATUS, "MARITAL_STATUS"); + + public static final DetectEntityEntityType CORPORATE_ACTION = + new DetectEntityEntityType(Value.CORPORATE_ACTION, "CORPORATE_ACTION"); + + public static final DetectEntityEntityType AGE = new DetectEntityEntityType(Value.AGE, "AGE"); + + public static final DetectEntityEntityType CVV = new DetectEntityEntityType(Value.CVV, "CVV"); + + public static final DetectEntityEntityType NAME_GIVEN = new DetectEntityEntityType(Value.NAME_GIVEN, "NAME_GIVEN"); + + public static final DetectEntityEntityType DATE = new DetectEntityEntityType(Value.DATE, "DATE"); + + public static final DetectEntityEntityType DAY = new DetectEntityEntityType(Value.DAY, "DAY"); + + public static final DetectEntityEntityType EFFECT = new DetectEntityEntityType(Value.EFFECT, "EFFECT"); + + public static final DetectEntityEntityType MEDICAL_CODE = + new DetectEntityEntityType(Value.MEDICAL_CODE, "MEDICAL_CODE"); + + public static final DetectEntityEntityType NUMERICAL_PII = + new DetectEntityEntityType(Value.NUMERICAL_PII, "NUMERICAL_PII"); + + public static final DetectEntityEntityType TREND = new DetectEntityEntityType(Value.TREND, "TREND"); + + public static final DetectEntityEntityType INJURY = new DetectEntityEntityType(Value.INJURY, "INJURY"); + + public static final DetectEntityEntityType DRIVER_LICENSE = + new DetectEntityEntityType(Value.DRIVER_LICENSE, "DRIVER_LICENSE"); + + public static final DetectEntityEntityType ALL = new DetectEntityEntityType(Value.ALL, "ALL"); + + public static final DetectEntityEntityType ORGANIZATION_ID = + new DetectEntityEntityType(Value.ORGANIZATION_ID, "ORGANIZATION_ID"); + + public static final DetectEntityEntityType PASSWORD = new DetectEntityEntityType(Value.PASSWORD, "PASSWORD"); + + public static final DetectEntityEntityType DOSE = new DetectEntityEntityType(Value.DOSE, "DOSE"); + + public static final DetectEntityEntityType NAME_FAMILY = + new DetectEntityEntityType(Value.NAME_FAMILY, "NAME_FAMILY"); + + public static final DetectEntityEntityType MONEY = new DetectEntityEntityType(Value.MONEY, "MONEY"); + + public static final DetectEntityEntityType URL = new DetectEntityEntityType(Value.URL, "URL"); + + public static final DetectEntityEntityType LOCATION_COORDINATE = + new DetectEntityEntityType(Value.LOCATION_COORDINATE, "LOCATION_COORDINATE"); + + public static final DetectEntityEntityType FINANCIAL_METRIC = + new DetectEntityEntityType(Value.FINANCIAL_METRIC, "FINANCIAL_METRIC"); + + public static final DetectEntityEntityType ORGANIZATION = + new DetectEntityEntityType(Value.ORGANIZATION, "ORGANIZATION"); + + public static final DetectEntityEntityType IP_ADDRESS = new DetectEntityEntityType(Value.IP_ADDRESS, "IP_ADDRESS"); + + public static final DetectEntityEntityType BLOOD_TYPE = new DetectEntityEntityType(Value.BLOOD_TYPE, "BLOOD_TYPE"); + + public static final DetectEntityEntityType MONTH = new DetectEntityEntityType(Value.MONTH, "MONTH"); + + public static final DetectEntityEntityType CREDIT_CARD = + new DetectEntityEntityType(Value.CREDIT_CARD, "CREDIT_CARD"); + + public static final DetectEntityEntityType ACCOUNT_NUMBER = + new DetectEntityEntityType(Value.ACCOUNT_NUMBER, "ACCOUNT_NUMBER"); + + public static final DetectEntityEntityType CREDIT_CARD_EXPIRATION = + new DetectEntityEntityType(Value.CREDIT_CARD_EXPIRATION, "CREDIT_CARD_EXPIRATION"); + + public static final DetectEntityEntityType SSN = new DetectEntityEntityType(Value.SSN, "SSN"); + + public static final DetectEntityEntityType EMAIL_ADDRESS = + new DetectEntityEntityType(Value.EMAIL_ADDRESS, "EMAIL_ADDRESS"); + + public static final DetectEntityEntityType STATISTICS = new DetectEntityEntityType(Value.STATISTICS, "STATISTICS"); + + private final Value value; + + private final String string; + + DetectEntityEntityType(Value value, String string) { + this.value = value; + this.string = string; + } + + public Value getEnumValue() { + return value; + } + + @java.lang.Override + @JsonValue + public String toString() { + return this.string; + } + + @java.lang.Override + public boolean equals(Object other) { + return (this == other) + || (other instanceof DetectEntityEntityType + && this.string.equals(((DetectEntityEntityType) other).string)); + } + + @java.lang.Override + public int hashCode() { + return this.string.hashCode(); + } + + public T visit(Visitor visitor) { + switch (value) { + case PHONE_NUMBER: + return visitor.visitPhoneNumber(); + case USERNAME: + return visitor.visitUsername(); + case VEHICLE_ID: + return visitor.visitVehicleId(); + case RELIGION: + return visitor.visitReligion(); + case ORIGIN: + return visitor.visitOrigin(); + case PASSPORT_NUMBER: + return visitor.visitPassportNumber(); + case YEAR: + return visitor.visitYear(); + case CONDITION: + return visitor.visitCondition(); + case LOCATION: + return visitor.visitLocation(); + case LOCATION_CITY: + return visitor.visitLocationCity(); + case LOCATION_COUNTRY: + return visitor.visitLocationCountry(); + case PROJECT: + return visitor.visitProject(); + case LOCATION_ZIP: + return visitor.visitLocationZip(); + case NAME_MEDICAL_PROFESSIONAL: + return visitor.visitNameMedicalProfessional(); + case BANK_ACCOUNT: + return visitor.visitBankAccount(); + case OCCUPATION: + return visitor.visitOccupation(); + case TIME: + return visitor.visitTime(); + case LOCATION_STATE: + return visitor.visitLocationState(); + case EVENT: + return visitor.visitEvent(); + case POLITICAL_AFFILIATION: + return visitor.visitPoliticalAffiliation(); + case LANGUAGE: + return visitor.visitLanguage(); + case ZODIAC_SIGN: + return visitor.visitZodiacSign(); + case PRODUCT: + return visitor.visitProduct(); + case SEXUALITY: + return visitor.visitSexuality(); + case DURATION: + return visitor.visitDuration(); + case PHYSICAL_ATTRIBUTE: + return visitor.visitPhysicalAttribute(); + case LOCATION_ADDRESS_STREET: + return visitor.visitLocationAddressStreet(); + case MEDICAL_PROCESS: + return visitor.visitMedicalProcess(); + case LOCATION_ADDRESS: + return visitor.visitLocationAddress(); + case FILENAME: + return visitor.visitFilename(); + case DOB: + return visitor.visitDob(); + case ORGANIZATION_MEDICAL_FACILITY: + return visitor.visitOrganizationMedicalFacility(); + case ROUTING_NUMBER: + return visitor.visitRoutingNumber(); + case HEALTHCARE_NUMBER: + return visitor.visitHealthcareNumber(); + case GENDER: + return visitor.visitGender(); + case NAME: + return visitor.visitName(); + case DRUG: + return visitor.visitDrug(); + case NONE: + return visitor.visitNone(); + case DATE_INTERVAL: + return visitor.visitDateInterval(); + case MARITAL_STATUS: + return visitor.visitMaritalStatus(); + case CORPORATE_ACTION: + return visitor.visitCorporateAction(); + case AGE: + return visitor.visitAge(); + case CVV: + return visitor.visitCvv(); + case NAME_GIVEN: + return visitor.visitNameGiven(); + case DATE: + return visitor.visitDate(); + case DAY: + return visitor.visitDay(); + case EFFECT: + return visitor.visitEffect(); + case MEDICAL_CODE: + return visitor.visitMedicalCode(); + case NUMERICAL_PII: + return visitor.visitNumericalPii(); + case TREND: + return visitor.visitTrend(); + case INJURY: + return visitor.visitInjury(); + case DRIVER_LICENSE: + return visitor.visitDriverLicense(); + case ALL: + return visitor.visitAll(); + case ORGANIZATION_ID: + return visitor.visitOrganizationId(); + case PASSWORD: + return visitor.visitPassword(); + case DOSE: + return visitor.visitDose(); + case NAME_FAMILY: + return visitor.visitNameFamily(); + case MONEY: + return visitor.visitMoney(); + case URL: + return visitor.visitUrl(); + case LOCATION_COORDINATE: + return visitor.visitLocationCoordinate(); + case FINANCIAL_METRIC: + return visitor.visitFinancialMetric(); + case ORGANIZATION: + return visitor.visitOrganization(); + case IP_ADDRESS: + return visitor.visitIpAddress(); + case BLOOD_TYPE: + return visitor.visitBloodType(); + case MONTH: + return visitor.visitMonth(); + case CREDIT_CARD: + return visitor.visitCreditCard(); + case ACCOUNT_NUMBER: + return visitor.visitAccountNumber(); + case CREDIT_CARD_EXPIRATION: + return visitor.visitCreditCardExpiration(); + case SSN: + return visitor.visitSsn(); + case EMAIL_ADDRESS: + return visitor.visitEmailAddress(); + case STATISTICS: + return visitor.visitStatistics(); + case UNKNOWN: + default: + return visitor.visitUnknown(string); + } + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static DetectEntityEntityType valueOf(String value) { + switch (value) { + case "PHONE_NUMBER": + return PHONE_NUMBER; + case "USERNAME": + return USERNAME; + case "VEHICLE_ID": + return VEHICLE_ID; + case "RELIGION": + return RELIGION; + case "ORIGIN": + return ORIGIN; + case "PASSPORT_NUMBER": + return PASSPORT_NUMBER; + case "YEAR": + return YEAR; + case "CONDITION": + return CONDITION; + case "LOCATION": + return LOCATION; + case "LOCATION_CITY": + return LOCATION_CITY; + case "LOCATION_COUNTRY": + return LOCATION_COUNTRY; + case "PROJECT": + return PROJECT; + case "LOCATION_ZIP": + return LOCATION_ZIP; + case "NAME_MEDICAL_PROFESSIONAL": + return NAME_MEDICAL_PROFESSIONAL; + case "BANK_ACCOUNT": + return BANK_ACCOUNT; + case "OCCUPATION": + return OCCUPATION; + case "TIME": + return TIME; + case "LOCATION_STATE": + return LOCATION_STATE; + case "EVENT": + return EVENT; + case "POLITICAL_AFFILIATION": + return POLITICAL_AFFILIATION; + case "LANGUAGE": + return LANGUAGE; + case "ZODIAC_SIGN": + return ZODIAC_SIGN; + case "PRODUCT": + return PRODUCT; + case "SEXUALITY": + return SEXUALITY; + case "DURATION": + return DURATION; + case "PHYSICAL_ATTRIBUTE": + return PHYSICAL_ATTRIBUTE; + case "LOCATION_ADDRESS_STREET": + return LOCATION_ADDRESS_STREET; + case "MEDICAL_PROCESS": + return MEDICAL_PROCESS; + case "LOCATION_ADDRESS": + return LOCATION_ADDRESS; + case "FILENAME": + return FILENAME; + case "DOB": + return DOB; + case "ORGANIZATION_MEDICAL_FACILITY": + return ORGANIZATION_MEDICAL_FACILITY; + case "ROUTING_NUMBER": + return ROUTING_NUMBER; + case "HEALTHCARE_NUMBER": + return HEALTHCARE_NUMBER; + case "GENDER": + return GENDER; + case "NAME": + return NAME; + case "DRUG": + return DRUG; + case "NONE": + return NONE; + case "DATE_INTERVAL": + return DATE_INTERVAL; + case "MARITAL_STATUS": + return MARITAL_STATUS; + case "CORPORATE_ACTION": + return CORPORATE_ACTION; + case "AGE": + return AGE; + case "CVV": + return CVV; + case "NAME_GIVEN": + return NAME_GIVEN; + case "DATE": + return DATE; + case "DAY": + return DAY; + case "EFFECT": + return EFFECT; + case "MEDICAL_CODE": + return MEDICAL_CODE; + case "NUMERICAL_PII": + return NUMERICAL_PII; + case "TREND": + return TREND; + case "INJURY": + return INJURY; + case "DRIVER_LICENSE": + return DRIVER_LICENSE; + case "ALL": + return ALL; + case "ORGANIZATION_ID": + return ORGANIZATION_ID; + case "PASSWORD": + return PASSWORD; + case "DOSE": + return DOSE; + case "NAME_FAMILY": + return NAME_FAMILY; + case "MONEY": + return MONEY; + case "URL": + return URL; + case "LOCATION_COORDINATE": + return LOCATION_COORDINATE; + case "FINANCIAL_METRIC": + return FINANCIAL_METRIC; + case "ORGANIZATION": + return ORGANIZATION; + case "IP_ADDRESS": + return IP_ADDRESS; + case "BLOOD_TYPE": + return BLOOD_TYPE; + case "MONTH": + return MONTH; + case "CREDIT_CARD": + return CREDIT_CARD; + case "ACCOUNT_NUMBER": + return ACCOUNT_NUMBER; + case "CREDIT_CARD_EXPIRATION": + return CREDIT_CARD_EXPIRATION; + case "SSN": + return SSN; + case "EMAIL_ADDRESS": + return EMAIL_ADDRESS; + case "STATISTICS": + return STATISTICS; + default: + return new DetectEntityEntityType(Value.UNKNOWN, value); + } + } + + public enum Value { + NONE, + + BANK_ACCOUNT, + + CREDIT_CARD, + + CREDIT_CARD_EXPIRATION, + + CVV, + + DATE, + + DATE_INTERVAL, + + DOB, + + DRIVER_LICENSE, + + EMAIL_ADDRESS, + + HEALTHCARE_NUMBER, + + IP_ADDRESS, + + LOCATION, + + NAME, + + NUMERICAL_PII, + + PHONE_NUMBER, + + SSN, + + URL, + + VEHICLE_ID, + + MEDICAL_CODE, + + NAME_FAMILY, + + NAME_GIVEN, + + ACCOUNT_NUMBER, + + EVENT, + + FILENAME, + + GENDER, + + LANGUAGE, + + LOCATION_ADDRESS, + + LOCATION_CITY, + + LOCATION_COORDINATE, + + LOCATION_COUNTRY, + + LOCATION_STATE, + + LOCATION_ZIP, + + MARITAL_STATUS, + + MONEY, + + NAME_MEDICAL_PROFESSIONAL, + + OCCUPATION, + + ORGANIZATION, + + ORGANIZATION_MEDICAL_FACILITY, + + ORIGIN, + + PASSPORT_NUMBER, + + PASSWORD, + + PHYSICAL_ATTRIBUTE, + + POLITICAL_AFFILIATION, + + RELIGION, + + TIME, + + USERNAME, + + ZODIAC_SIGN, + + BLOOD_TYPE, + + CONDITION, + + DOSE, + + DRUG, + + INJURY, + + MEDICAL_PROCESS, + + STATISTICS, + + ROUTING_NUMBER, + + CORPORATE_ACTION, + + FINANCIAL_METRIC, + + PRODUCT, + + TREND, + + DURATION, + + LOCATION_ADDRESS_STREET, + + AGE, + + SEXUALITY, + + EFFECT, + + PROJECT, + + ORGANIZATION_ID, + + DAY, + + MONTH, + + YEAR, + + ALL, + + UNKNOWN + } + + public interface Visitor { + T visitNone(); + + T visitBankAccount(); + + T visitCreditCard(); + + T visitCreditCardExpiration(); + + T visitCvv(); + + T visitDate(); + + T visitDateInterval(); + + T visitDob(); + + T visitDriverLicense(); + + T visitEmailAddress(); + + T visitHealthcareNumber(); + + T visitIpAddress(); + + T visitLocation(); + + T visitName(); + + T visitNumericalPii(); + + T visitPhoneNumber(); + + T visitSsn(); + + T visitUrl(); + + T visitVehicleId(); + + T visitMedicalCode(); + + T visitNameFamily(); + + T visitNameGiven(); + + T visitAccountNumber(); + + T visitEvent(); + + T visitFilename(); + + T visitGender(); + + T visitLanguage(); + + T visitLocationAddress(); + + T visitLocationCity(); + + T visitLocationCoordinate(); + + T visitLocationCountry(); + + T visitLocationState(); + + T visitLocationZip(); + + T visitMaritalStatus(); + + T visitMoney(); + + T visitNameMedicalProfessional(); + + T visitOccupation(); + + T visitOrganization(); + + T visitOrganizationMedicalFacility(); + + T visitOrigin(); + + T visitPassportNumber(); + + T visitPassword(); + + T visitPhysicalAttribute(); + + T visitPoliticalAffiliation(); + + T visitReligion(); + + T visitTime(); + + T visitUsername(); + + T visitZodiacSign(); + + T visitBloodType(); + + T visitCondition(); + + T visitDose(); + + T visitDrug(); + + T visitInjury(); + + T visitMedicalProcess(); + + T visitStatistics(); + + T visitRoutingNumber(); + + T visitCorporateAction(); + + T visitFinancialMetric(); + + T visitProduct(); + + T visitTrend(); + + T visitDuration(); + + T visitLocationAddressStreet(); + + T visitAge(); + + T visitSexuality(); + + T visitEffect(); + + T visitProject(); + + T visitOrganizationId(); + + T visitDay(); + + T visitMonth(); + + T visitYear(); + + T visitAll(); + + T visitUnknown(String unknownType); + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectGuardrailsResponseV2.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectGuardrailsResponseV2.java new file mode 100644 index 00000000..ca14dc32 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectGuardrailsResponseV2.java @@ -0,0 +1,252 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import org.jetbrains.annotations.NotNull; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = DetectGuardrailsResponseV2.Builder.class) +public final class DetectGuardrailsResponseV2 { + private final String text; + + private final Optional toxic; + + private final Optional deniedTopic; + + private final DetectGuardrailsResponseV2Validation validation; + + private final Map additionalProperties; + + private DetectGuardrailsResponseV2( + String text, + Optional toxic, + Optional deniedTopic, + DetectGuardrailsResponseV2Validation validation, + Map additionalProperties) { + this.text = text; + this.toxic = toxic; + this.deniedTopic = deniedTopic; + this.validation = validation; + this.additionalProperties = additionalProperties; + } + + /** + * @return Text that was checked against guardrails. + */ + @JsonProperty("text") + public String getText() { + return text; + } + + /** + * @return Whether the text is toxic. + */ + @JsonProperty("toxic") + public Optional getToxic() { + return toxic; + } + + /** + * @return Whether the text included a denied topic. + */ + @JsonProperty("deniedTopic") + public Optional getDeniedTopic() { + return deniedTopic; + } + + /** + * @return Whether the text passed validation. + */ + @JsonProperty("validation") + public DetectGuardrailsResponseV2Validation getValidation() { + return validation; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof DetectGuardrailsResponseV2 && equalTo((DetectGuardrailsResponseV2) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(DetectGuardrailsResponseV2 other) { + return text.equals(other.text) + && toxic.equals(other.toxic) + && deniedTopic.equals(other.deniedTopic) + && validation.equals(other.validation); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.text, this.toxic, this.deniedTopic, this.validation); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static TextStage builder() { + return new Builder(); + } + + public interface TextStage { + /** + *

Text that was checked against guardrails.

+ */ + ValidationStage text(@NotNull String text); + + Builder from(DetectGuardrailsResponseV2 other); + } + + public interface ValidationStage { + /** + *

Whether the text passed validation.

+ */ + _FinalStage validation(@NotNull DetectGuardrailsResponseV2Validation validation); + } + + public interface _FinalStage { + DetectGuardrailsResponseV2 build(); + + _FinalStage additionalProperty(String key, Object value); + + _FinalStage additionalProperties(Map additionalProperties); + + /** + *

Whether the text is toxic.

+ */ + _FinalStage toxic(Optional toxic); + + _FinalStage toxic(Boolean toxic); + + /** + *

Whether the text included a denied topic.

+ */ + _FinalStage deniedTopic(Optional deniedTopic); + + _FinalStage deniedTopic(Boolean deniedTopic); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder implements TextStage, ValidationStage, _FinalStage { + private String text; + + private DetectGuardrailsResponseV2Validation validation; + + private Optional deniedTopic = Optional.empty(); + + private Optional toxic = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + @java.lang.Override + public Builder from(DetectGuardrailsResponseV2 other) { + text(other.getText()); + toxic(other.getToxic()); + deniedTopic(other.getDeniedTopic()); + validation(other.getValidation()); + return this; + } + + /** + *

Text that was checked against guardrails.

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonSetter("text") + public ValidationStage text(@NotNull String text) { + this.text = Objects.requireNonNull(text, "text must not be null"); + return this; + } + + /** + *

Whether the text passed validation.

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonSetter("validation") + public _FinalStage validation(@NotNull DetectGuardrailsResponseV2Validation validation) { + this.validation = Objects.requireNonNull(validation, "validation must not be null"); + return this; + } + + /** + *

Whether the text included a denied topic.

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + public _FinalStage deniedTopic(Boolean deniedTopic) { + this.deniedTopic = Optional.ofNullable(deniedTopic); + return this; + } + + /** + *

Whether the text included a denied topic.

+ */ + @java.lang.Override + @JsonSetter(value = "deniedTopic", nulls = Nulls.SKIP) + public _FinalStage deniedTopic(Optional deniedTopic) { + this.deniedTopic = deniedTopic; + return this; + } + + /** + *

Whether the text is toxic.

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + public _FinalStage toxic(Boolean toxic) { + this.toxic = Optional.ofNullable(toxic); + return this; + } + + /** + *

Whether the text is toxic.

+ */ + @java.lang.Override + @JsonSetter(value = "toxic", nulls = Nulls.SKIP) + public _FinalStage toxic(Optional toxic) { + this.toxic = toxic; + return this; + } + + @java.lang.Override + public DetectGuardrailsResponseV2 build() { + return new DetectGuardrailsResponseV2(text, toxic, deniedTopic, validation, additionalProperties); + } + + @java.lang.Override + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + @java.lang.Override + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectGuardrailsResponseV2Validation.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectGuardrailsResponseV2Validation.java new file mode 100644 index 00000000..0337a29f --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectGuardrailsResponseV2Validation.java @@ -0,0 +1,86 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +public final class DetectGuardrailsResponseV2Validation { + public static final DetectGuardrailsResponseV2Validation PASSED = + new DetectGuardrailsResponseV2Validation(Value.PASSED, "PASSED"); + + public static final DetectGuardrailsResponseV2Validation FAILED = + new DetectGuardrailsResponseV2Validation(Value.FAILED, "FAILED"); + + private final Value value; + + private final String string; + + DetectGuardrailsResponseV2Validation(Value value, String string) { + this.value = value; + this.string = string; + } + + public Value getEnumValue() { + return value; + } + + @java.lang.Override + @JsonValue + public String toString() { + return this.string; + } + + @java.lang.Override + public boolean equals(Object other) { + return (this == other) + || (other instanceof DetectGuardrailsResponseV2Validation + && this.string.equals(((DetectGuardrailsResponseV2Validation) other).string)); + } + + @java.lang.Override + public int hashCode() { + return this.string.hashCode(); + } + + public T visit(Visitor visitor) { + switch (value) { + case PASSED: + return visitor.visitPassed(); + case FAILED: + return visitor.visitFailed(); + case UNKNOWN: + default: + return visitor.visitUnknown(string); + } + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static DetectGuardrailsResponseV2Validation valueOf(String value) { + switch (value) { + case "PASSED": + return PASSED; + case "FAILED": + return FAILED; + default: + return new DetectGuardrailsResponseV2Validation(Value.UNKNOWN, value); + } + } + + public enum Value { + FAILED, + + PASSED, + + UNKNOWN + } + + public interface Visitor { + T visitFailed(); + + T visitPassed(); + + T visitUnknown(String unknownType); + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectObjectEntity.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectObjectEntity.java new file mode 100644 index 00000000..52333c00 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectObjectEntity.java @@ -0,0 +1,142 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = DetectObjectEntity.Builder.class) +public final class DetectObjectEntity { + private final Optional entityType; + + private final Optional deidentificationType; + + private final Map additionalProperties; + + private DetectObjectEntity( + Optional entityType, + Optional deidentificationType, + Map additionalProperties) { + this.entityType = entityType; + this.deidentificationType = deidentificationType; + this.additionalProperties = additionalProperties; + } + + /** + * @return Type of entity to detect. + */ + @JsonProperty("entityType") + public Optional getEntityType() { + return entityType; + } + + /** + * @return Type of deidentification to apply. + */ + @JsonProperty("deidentificationType") + public Optional getDeidentificationType() { + return deidentificationType; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof DetectObjectEntity && equalTo((DetectObjectEntity) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(DetectObjectEntity other) { + return entityType.equals(other.entityType) && deidentificationType.equals(other.deidentificationType); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.entityType, this.deidentificationType); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + private Optional entityType = Optional.empty(); + + private Optional deidentificationType = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(DetectObjectEntity other) { + entityType(other.getEntityType()); + deidentificationType(other.getDeidentificationType()); + return this; + } + + /** + *

Type of entity to detect.

+ */ + @JsonSetter(value = "entityType", nulls = Nulls.SKIP) + public Builder entityType(Optional entityType) { + this.entityType = entityType; + return this; + } + + public Builder entityType(DetectObjectEntityEntityType entityType) { + this.entityType = Optional.ofNullable(entityType); + return this; + } + + /** + *

Type of deidentification to apply.

+ */ + @JsonSetter(value = "deidentificationType", nulls = Nulls.SKIP) + public Builder deidentificationType(Optional deidentificationType) { + this.deidentificationType = deidentificationType; + return this; + } + + public Builder deidentificationType(DetectObjectEntityDeidentificationType deidentificationType) { + this.deidentificationType = Optional.ofNullable(deidentificationType); + return this; + } + + public DetectObjectEntity build() { + return new DetectObjectEntity(entityType, deidentificationType, additionalProperties); + } + + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectObjectEntityDeidentificationType.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectObjectEntityDeidentificationType.java new file mode 100644 index 00000000..aec7dc98 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectObjectEntityDeidentificationType.java @@ -0,0 +1,97 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +public final class DetectObjectEntityDeidentificationType { + public static final DetectObjectEntityDeidentificationType UNREDACT = + new DetectObjectEntityDeidentificationType(Value.UNREDACT, "UNREDACT"); + + public static final DetectObjectEntityDeidentificationType UNKNOWN = + new DetectObjectEntityDeidentificationType(Value.UNKNOWN, "UNKNOWN"); + + public static final DetectObjectEntityDeidentificationType REDACT = + new DetectObjectEntityDeidentificationType(Value.REDACT, "REDACT"); + + private final Value value; + + private final String string; + + DetectObjectEntityDeidentificationType(Value value, String string) { + this.value = value; + this.string = string; + } + + public Value getEnumValue() { + return value; + } + + @java.lang.Override + @JsonValue + public String toString() { + return this.string; + } + + @java.lang.Override + public boolean equals(Object other) { + return (this == other) + || (other instanceof DetectObjectEntityDeidentificationType + && this.string.equals(((DetectObjectEntityDeidentificationType) other).string)); + } + + @java.lang.Override + public int hashCode() { + return this.string.hashCode(); + } + + public T visit(Visitor visitor) { + switch (value) { + case UNREDACT: + return visitor.visitUnredact(); + case UNKNOWN: + return visitor.visitUnknown(); + case REDACT: + return visitor.visitRedact(); + case _UNKNOWN: + default: + return visitor.visitUnknown(string); + } + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static DetectObjectEntityDeidentificationType valueOf(String value) { + switch (value) { + case "UNREDACT": + return UNREDACT; + case "UNKNOWN": + return UNKNOWN; + case "REDACT": + return REDACT; + default: + return new DetectObjectEntityDeidentificationType(Value._UNKNOWN, value); + } + } + + public enum Value { + UNKNOWN, + + REDACT, + + UNREDACT, + + _UNKNOWN + } + + public interface Visitor { + T visitUnknown(); + + T visitRedact(); + + T visitUnredact(); + + T visitUnknown(String unknownType); + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectObjectEntityEntityType.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectObjectEntityEntityType.java new file mode 100644 index 00000000..93f8b64f --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectObjectEntityEntityType.java @@ -0,0 +1,126 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +public final class DetectObjectEntityEntityType { + public static final DetectObjectEntityEntityType FACE = new DetectObjectEntityEntityType(Value.FACE, "FACE"); + + public static final DetectObjectEntityEntityType SIGNATURE = + new DetectObjectEntityEntityType(Value.SIGNATURE, "SIGNATURE"); + + public static final DetectObjectEntityEntityType LICENSE_PLATE = + new DetectObjectEntityEntityType(Value.LICENSE_PLATE, "LICENSE_PLATE"); + + public static final DetectObjectEntityEntityType ALL = new DetectObjectEntityEntityType(Value.ALL, "ALL"); + + public static final DetectObjectEntityEntityType NONE = new DetectObjectEntityEntityType(Value.NONE, "NONE"); + + public static final DetectObjectEntityEntityType LOGO = new DetectObjectEntityEntityType(Value.LOGO, "LOGO"); + + private final Value value; + + private final String string; + + DetectObjectEntityEntityType(Value value, String string) { + this.value = value; + this.string = string; + } + + public Value getEnumValue() { + return value; + } + + @java.lang.Override + @JsonValue + public String toString() { + return this.string; + } + + @java.lang.Override + public boolean equals(Object other) { + return (this == other) + || (other instanceof DetectObjectEntityEntityType + && this.string.equals(((DetectObjectEntityEntityType) other).string)); + } + + @java.lang.Override + public int hashCode() { + return this.string.hashCode(); + } + + public T visit(Visitor visitor) { + switch (value) { + case FACE: + return visitor.visitFace(); + case SIGNATURE: + return visitor.visitSignature(); + case LICENSE_PLATE: + return visitor.visitLicensePlate(); + case ALL: + return visitor.visitAll(); + case NONE: + return visitor.visitNone(); + case LOGO: + return visitor.visitLogo(); + case UNKNOWN: + default: + return visitor.visitUnknown(string); + } + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static DetectObjectEntityEntityType valueOf(String value) { + switch (value) { + case "FACE": + return FACE; + case "SIGNATURE": + return SIGNATURE; + case "LICENSE_PLATE": + return LICENSE_PLATE; + case "ALL": + return ALL; + case "NONE": + return NONE; + case "LOGO": + return LOGO; + default: + return new DetectObjectEntityEntityType(Value.UNKNOWN, value); + } + } + + public enum Value { + NONE, + + FACE, + + LICENSE_PLATE, + + LOGO, + + SIGNATURE, + + ALL, + + UNKNOWN + } + + public interface Visitor { + T visitNone(); + + T visitFace(); + + T visitLicensePlate(); + + T visitLogo(); + + T visitSignature(); + + T visitAll(); + + T visitUnknown(String unknownType); + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectReturnEntities.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectReturnEntities.java new file mode 100644 index 00000000..f5435ec2 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectReturnEntities.java @@ -0,0 +1,94 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +public final class DetectReturnEntities { + public static final DetectReturnEntities ALL = new DetectReturnEntities(Value.ALL, "ALL"); + + public static final DetectReturnEntities NONE = new DetectReturnEntities(Value.NONE, "NONE"); + + public static final DetectReturnEntities EXCLUDE_SENSITIVE_DATA = + new DetectReturnEntities(Value.EXCLUDE_SENSITIVE_DATA, "EXCLUDE_SENSITIVE_DATA"); + + private final Value value; + + private final String string; + + DetectReturnEntities(Value value, String string) { + this.value = value; + this.string = string; + } + + public Value getEnumValue() { + return value; + } + + @java.lang.Override + @JsonValue + public String toString() { + return this.string; + } + + @java.lang.Override + public boolean equals(Object other) { + return (this == other) + || (other instanceof DetectReturnEntities && this.string.equals(((DetectReturnEntities) other).string)); + } + + @java.lang.Override + public int hashCode() { + return this.string.hashCode(); + } + + public T visit(Visitor visitor) { + switch (value) { + case ALL: + return visitor.visitAll(); + case NONE: + return visitor.visitNone(); + case EXCLUDE_SENSITIVE_DATA: + return visitor.visitExcludeSensitiveData(); + case UNKNOWN: + default: + return visitor.visitUnknown(string); + } + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static DetectReturnEntities valueOf(String value) { + switch (value) { + case "ALL": + return ALL; + case "NONE": + return NONE; + case "EXCLUDE_SENSITIVE_DATA": + return EXCLUDE_SENSITIVE_DATA; + default: + return new DetectReturnEntities(Value.UNKNOWN, value); + } + } + + public enum Value { + EXCLUDE_SENSITIVE_DATA, + + ALL, + + NONE, + + UNKNOWN + } + + public interface Visitor { + T visitExcludeSensitiveData(); + + T visitAll(); + + T visitNone(); + + T visitUnknown(String unknownType); + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectRunsResponseV2.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectRunsResponseV2.java new file mode 100644 index 00000000..372e164d --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectRunsResponseV2.java @@ -0,0 +1,228 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = DetectRunsResponseV2.Builder.class) +public final class DetectRunsResponseV2 { + private final Optional status; + + private final Optional outputType; + + private final Optional> output; + + private final Optional message; + + private final Optional metrics; + + private final Map additionalProperties; + + private DetectRunsResponseV2( + Optional status, + Optional outputType, + Optional> output, + Optional message, + Optional metrics, + Map additionalProperties) { + this.status = status; + this.outputType = outputType; + this.output = output; + this.message = message; + this.metrics = metrics; + this.additionalProperties = additionalProperties; + } + + /** + * @return Status of the detect run. + */ + @JsonProperty("status") + public Optional getStatus() { + return status; + } + + /** + * @return Data source of the output file. BASE64: Base64-encoded file string. SKYFLOW_ID: Reference file by vault ID. PRESIGNED_URL: S3 presigned URL of input file. + */ + @JsonProperty("outputType") + public Optional getOutputType() { + return outputType; + } + + /** + * @return Details of output files. + */ + @JsonProperty("output") + public Optional> getOutput() { + return output; + } + + /** + * @return Status details about the Detect run. + */ + @JsonProperty("message") + public Optional getMessage() { + return message; + } + + @JsonProperty("metrics") + public Optional getMetrics() { + return metrics; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof DetectRunsResponseV2 && equalTo((DetectRunsResponseV2) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(DetectRunsResponseV2 other) { + return status.equals(other.status) + && outputType.equals(other.outputType) + && output.equals(other.output) + && message.equals(other.message) + && metrics.equals(other.metrics); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.status, this.outputType, this.output, this.message, this.metrics); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + private Optional status = Optional.empty(); + + private Optional outputType = Optional.empty(); + + private Optional> output = Optional.empty(); + + private Optional message = Optional.empty(); + + private Optional metrics = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(DetectRunsResponseV2 other) { + status(other.getStatus()); + outputType(other.getOutputType()); + output(other.getOutput()); + message(other.getMessage()); + metrics(other.getMetrics()); + return this; + } + + /** + *

Status of the detect run.

+ */ + @JsonSetter(value = "status", nulls = Nulls.SKIP) + public Builder status(Optional status) { + this.status = status; + return this; + } + + public Builder status(DetectRunsResponseV2Status status) { + this.status = Optional.ofNullable(status); + return this; + } + + /** + *

Data source of the output file. BASE64: Base64-encoded file string. SKYFLOW_ID: Reference file by vault ID. PRESIGNED_URL: S3 presigned URL of input file.

+ */ + @JsonSetter(value = "outputType", nulls = Nulls.SKIP) + public Builder outputType(Optional outputType) { + this.outputType = outputType; + return this; + } + + public Builder outputType(DetectRunsResponseV2OutputType outputType) { + this.outputType = Optional.ofNullable(outputType); + return this; + } + + /** + *

Details of output files.

+ */ + @JsonSetter(value = "output", nulls = Nulls.SKIP) + public Builder output(Optional> output) { + this.output = output; + return this; + } + + public Builder output(List output) { + this.output = Optional.ofNullable(output); + return this; + } + + /** + *

Status details about the Detect run.

+ */ + @JsonSetter(value = "message", nulls = Nulls.SKIP) + public Builder message(Optional message) { + this.message = message; + return this; + } + + public Builder message(String message) { + this.message = Optional.ofNullable(message); + return this; + } + + @JsonSetter(value = "metrics", nulls = Nulls.SKIP) + public Builder metrics(Optional metrics) { + this.metrics = metrics; + return this; + } + + public Builder metrics(Metrics metrics) { + this.metrics = Optional.ofNullable(metrics); + return this; + } + + public DetectRunsResponseV2 build() { + return new DetectRunsResponseV2(status, outputType, output, message, metrics, additionalProperties); + } + + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectRunsResponseV2OutputType.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectRunsResponseV2OutputType.java new file mode 100644 index 00000000..cb995d80 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectRunsResponseV2OutputType.java @@ -0,0 +1,97 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +public final class DetectRunsResponseV2OutputType { + public static final DetectRunsResponseV2OutputType BASE64 = + new DetectRunsResponseV2OutputType(Value.BASE64, "BASE64"); + + public static final DetectRunsResponseV2OutputType SKYFLOW_ID = + new DetectRunsResponseV2OutputType(Value.SKYFLOW_ID, "SKYFLOW_ID"); + + public static final DetectRunsResponseV2OutputType PRESIGNED_URL = + new DetectRunsResponseV2OutputType(Value.PRESIGNED_URL, "PRESIGNED_URL"); + + private final Value value; + + private final String string; + + DetectRunsResponseV2OutputType(Value value, String string) { + this.value = value; + this.string = string; + } + + public Value getEnumValue() { + return value; + } + + @java.lang.Override + @JsonValue + public String toString() { + return this.string; + } + + @java.lang.Override + public boolean equals(Object other) { + return (this == other) + || (other instanceof DetectRunsResponseV2OutputType + && this.string.equals(((DetectRunsResponseV2OutputType) other).string)); + } + + @java.lang.Override + public int hashCode() { + return this.string.hashCode(); + } + + public T visit(Visitor visitor) { + switch (value) { + case BASE64: + return visitor.visitBase64(); + case SKYFLOW_ID: + return visitor.visitSkyflowId(); + case PRESIGNED_URL: + return visitor.visitPresignedUrl(); + case UNKNOWN: + default: + return visitor.visitUnknown(string); + } + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static DetectRunsResponseV2OutputType valueOf(String value) { + switch (value) { + case "BASE64": + return BASE64; + case "SKYFLOW_ID": + return SKYFLOW_ID; + case "PRESIGNED_URL": + return PRESIGNED_URL; + default: + return new DetectRunsResponseV2OutputType(Value.UNKNOWN, value); + } + } + + public enum Value { + BASE64, + + SKYFLOW_ID, + + PRESIGNED_URL, + + UNKNOWN + } + + public interface Visitor { + T visitBase64(); + + T visitSkyflowId(); + + T visitPresignedUrl(); + + T visitUnknown(String unknownType); + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectRunsResponseV2Status.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectRunsResponseV2Status.java new file mode 100644 index 00000000..c4549699 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/DetectRunsResponseV2Status.java @@ -0,0 +1,115 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +public final class DetectRunsResponseV2Status { + public static final DetectRunsResponseV2Status IN_PROGRESS = + new DetectRunsResponseV2Status(Value.IN_PROGRESS, "IN_PROGRESS"); + + public static final DetectRunsResponseV2Status QUEUED = new DetectRunsResponseV2Status(Value.QUEUED, "QUEUED"); + + public static final DetectRunsResponseV2Status SUCCESS = new DetectRunsResponseV2Status(Value.SUCCESS, "SUCCESS"); + + public static final DetectRunsResponseV2Status UNKNOWN = new DetectRunsResponseV2Status(Value.UNKNOWN, "UNKNOWN"); + + public static final DetectRunsResponseV2Status FAILED = new DetectRunsResponseV2Status(Value.FAILED, "FAILED"); + + private final Value value; + + private final String string; + + DetectRunsResponseV2Status(Value value, String string) { + this.value = value; + this.string = string; + } + + public Value getEnumValue() { + return value; + } + + @java.lang.Override + @JsonValue + public String toString() { + return this.string; + } + + @java.lang.Override + public boolean equals(Object other) { + return (this == other) + || (other instanceof DetectRunsResponseV2Status + && this.string.equals(((DetectRunsResponseV2Status) other).string)); + } + + @java.lang.Override + public int hashCode() { + return this.string.hashCode(); + } + + public T visit(Visitor visitor) { + switch (value) { + case IN_PROGRESS: + return visitor.visitInProgress(); + case QUEUED: + return visitor.visitQueued(); + case SUCCESS: + return visitor.visitSuccess(); + case UNKNOWN: + return visitor.visitUnknown(); + case FAILED: + return visitor.visitFailed(); + case _UNKNOWN: + default: + return visitor.visitUnknown(string); + } + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static DetectRunsResponseV2Status valueOf(String value) { + switch (value) { + case "IN_PROGRESS": + return IN_PROGRESS; + case "QUEUED": + return QUEUED; + case "SUCCESS": + return SUCCESS; + case "UNKNOWN": + return UNKNOWN; + case "FAILED": + return FAILED; + default: + return new DetectRunsResponseV2Status(Value._UNKNOWN, value); + } + } + + public enum Value { + UNKNOWN, + + FAILED, + + SUCCESS, + + IN_PROGRESS, + + QUEUED, + + _UNKNOWN + } + + public interface Visitor { + T visitUnknown(); + + T visitFailed(); + + T visitSuccess(); + + T visitInProgress(); + + T visitQueued(); + + T visitUnknown(String unknownType); + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/DocumentV2.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/DocumentV2.java new file mode 100644 index 00000000..4e080422 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/DocumentV2.java @@ -0,0 +1,105 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = DocumentV2.Builder.class) +public final class DocumentV2 { + private final Optional pdf; + + private final Map additionalProperties; + + private DocumentV2(Optional pdf, Map additionalProperties) { + this.pdf = pdf; + this.additionalProperties = additionalProperties; + } + + @JsonProperty("pdf") + public Optional getPdf() { + return pdf; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof DocumentV2 && equalTo((DocumentV2) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(DocumentV2 other) { + return pdf.equals(other.pdf); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.pdf); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + private Optional pdf = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(DocumentV2 other) { + pdf(other.getPdf()); + return this; + } + + @JsonSetter(value = "pdf", nulls = Nulls.SKIP) + public Builder pdf(Optional pdf) { + this.pdf = pdf; + return this; + } + + public Builder pdf(DocumentV2Pdf pdf) { + this.pdf = Optional.ofNullable(pdf); + return this; + } + + public DocumentV2 build() { + return new DocumentV2(pdf, additionalProperties); + } + + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/DocumentV2Pdf.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/DocumentV2Pdf.java new file mode 100644 index 00000000..4aae1940 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/DocumentV2Pdf.java @@ -0,0 +1,173 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = DocumentV2Pdf.Builder.class) +public final class DocumentV2Pdf { + private final Optional processingMode; + + private final Optional density; + + private final Optional maxResolution; + + private final Map additionalProperties; + + private DocumentV2Pdf( + Optional processingMode, + Optional density, + Optional maxResolution, + Map additionalProperties) { + this.processingMode = processingMode; + this.density = density; + this.maxResolution = maxResolution; + this.additionalProperties = additionalProperties; + } + + /** + * @return Processing mode for the PDF file. TEXT_LAYER: processes the PDF's native text layer and replaces detected entities with entity tokens (for example, [NAME_1]). Fast and accurate for PDFs with selectable text. OCR: processes the PDF as an image using optical character recognition and covers detected content with a gray box. Use for scanned or image-based PDFs. Because OCR rasterizes each page, the output PDF file size can be significantly larger than the input. + */ + @JsonProperty("processingMode") + public Optional getProcessingMode() { + return processingMode; + } + + /** + * @return Pixel density at which to process the PDF file. Applies only when processingMode is OCR. + */ + @JsonProperty("density") + public Optional getDensity() { + return density; + } + + /** + * @return Max resolution at which to process the PDF file. Applies only when processingMode is OCR. + */ + @JsonProperty("maxResolution") + public Optional getMaxResolution() { + return maxResolution; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof DocumentV2Pdf && equalTo((DocumentV2Pdf) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(DocumentV2Pdf other) { + return processingMode.equals(other.processingMode) + && density.equals(other.density) + && maxResolution.equals(other.maxResolution); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.processingMode, this.density, this.maxResolution); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + private Optional processingMode = Optional.empty(); + + private Optional density = Optional.empty(); + + private Optional maxResolution = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(DocumentV2Pdf other) { + processingMode(other.getProcessingMode()); + density(other.getDensity()); + maxResolution(other.getMaxResolution()); + return this; + } + + /** + *

Processing mode for the PDF file. TEXT_LAYER: processes the PDF's native text layer and replaces detected entities with entity tokens (for example, [NAME_1]). Fast and accurate for PDFs with selectable text. OCR: processes the PDF as an image using optical character recognition and covers detected content with a gray box. Use for scanned or image-based PDFs. Because OCR rasterizes each page, the output PDF file size can be significantly larger than the input.

+ */ + @JsonSetter(value = "processingMode", nulls = Nulls.SKIP) + public Builder processingMode(Optional processingMode) { + this.processingMode = processingMode; + return this; + } + + public Builder processingMode(DocumentV2PdfProcessingMode processingMode) { + this.processingMode = Optional.ofNullable(processingMode); + return this; + } + + /** + *

Pixel density at which to process the PDF file. Applies only when processingMode is OCR.

+ */ + @JsonSetter(value = "density", nulls = Nulls.SKIP) + public Builder density(Optional density) { + this.density = density; + return this; + } + + public Builder density(Integer density) { + this.density = Optional.ofNullable(density); + return this; + } + + /** + *

Max resolution at which to process the PDF file. Applies only when processingMode is OCR.

+ */ + @JsonSetter(value = "maxResolution", nulls = Nulls.SKIP) + public Builder maxResolution(Optional maxResolution) { + this.maxResolution = maxResolution; + return this; + } + + public Builder maxResolution(Integer maxResolution) { + this.maxResolution = Optional.ofNullable(maxResolution); + return this; + } + + public DocumentV2Pdf build() { + return new DocumentV2Pdf(processingMode, density, maxResolution, additionalProperties); + } + + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/DocumentV2PdfProcessingMode.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/DocumentV2PdfProcessingMode.java new file mode 100644 index 00000000..2ada581f --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/DocumentV2PdfProcessingMode.java @@ -0,0 +1,95 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +public final class DocumentV2PdfProcessingMode { + public static final DocumentV2PdfProcessingMode OCR = new DocumentV2PdfProcessingMode(Value.OCR, "OCR"); + + public static final DocumentV2PdfProcessingMode NONE = new DocumentV2PdfProcessingMode(Value.NONE, "NONE"); + + public static final DocumentV2PdfProcessingMode TEXT_LAYER = + new DocumentV2PdfProcessingMode(Value.TEXT_LAYER, "TEXT_LAYER"); + + private final Value value; + + private final String string; + + DocumentV2PdfProcessingMode(Value value, String string) { + this.value = value; + this.string = string; + } + + public Value getEnumValue() { + return value; + } + + @java.lang.Override + @JsonValue + public String toString() { + return this.string; + } + + @java.lang.Override + public boolean equals(Object other) { + return (this == other) + || (other instanceof DocumentV2PdfProcessingMode + && this.string.equals(((DocumentV2PdfProcessingMode) other).string)); + } + + @java.lang.Override + public int hashCode() { + return this.string.hashCode(); + } + + public T visit(Visitor visitor) { + switch (value) { + case OCR: + return visitor.visitOcr(); + case NONE: + return visitor.visitNone(); + case TEXT_LAYER: + return visitor.visitTextLayer(); + case UNKNOWN: + default: + return visitor.visitUnknown(string); + } + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static DocumentV2PdfProcessingMode valueOf(String value) { + switch (value) { + case "OCR": + return OCR; + case "NONE": + return NONE; + case "TEXT_LAYER": + return TEXT_LAYER; + default: + return new DocumentV2PdfProcessingMode(Value.UNKNOWN, value); + } + } + + public enum Value { + NONE, + + OCR, + + TEXT_LAYER, + + UNKNOWN + } + + public interface Visitor { + T visitNone(); + + T visitOcr(); + + T visitTextLayer(); + + T visitUnknown(String unknownType); + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/EntityTransformation.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/EntityTransformation.java new file mode 100644 index 00000000..870b3e13 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/EntityTransformation.java @@ -0,0 +1,106 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = EntityTransformation.Builder.class) +public final class EntityTransformation { + private final Optional shiftDates; + + private final Map additionalProperties; + + private EntityTransformation( + Optional shiftDates, Map additionalProperties) { + this.shiftDates = shiftDates; + this.additionalProperties = additionalProperties; + } + + @JsonProperty("shiftDates") + public Optional getShiftDates() { + return shiftDates; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof EntityTransformation && equalTo((EntityTransformation) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(EntityTransformation other) { + return shiftDates.equals(other.shiftDates); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.shiftDates); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + private Optional shiftDates = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(EntityTransformation other) { + shiftDates(other.getShiftDates()); + return this; + } + + @JsonSetter(value = "shiftDates", nulls = Nulls.SKIP) + public Builder shiftDates(Optional shiftDates) { + this.shiftDates = shiftDates; + return this; + } + + public Builder shiftDates(TransformationShiftDates shiftDates) { + this.shiftDates = Optional.ofNullable(shiftDates); + return this; + } + + public EntityTransformation build() { + return new EntityTransformation(shiftDates, additionalProperties); + } + + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/ErrorResponse.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/ErrorResponse.java new file mode 100644 index 00000000..4e29036e --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/ErrorResponse.java @@ -0,0 +1,118 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import org.jetbrains.annotations.NotNull; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = ErrorResponse.Builder.class) +public final class ErrorResponse { + private final ErrorResponseError error; + + private final Map additionalProperties; + + private ErrorResponse(ErrorResponseError error, Map additionalProperties) { + this.error = error; + this.additionalProperties = additionalProperties; + } + + @JsonProperty("error") + public ErrorResponseError getError() { + return error; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof ErrorResponse && equalTo((ErrorResponse) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(ErrorResponse other) { + return error.equals(other.error); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.error); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static ErrorStage builder() { + return new Builder(); + } + + public interface ErrorStage { + _FinalStage error(@NotNull ErrorResponseError error); + + Builder from(ErrorResponse other); + } + + public interface _FinalStage { + ErrorResponse build(); + + _FinalStage additionalProperty(String key, Object value); + + _FinalStage additionalProperties(Map additionalProperties); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder implements ErrorStage, _FinalStage { + private ErrorResponseError error; + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + @java.lang.Override + public Builder from(ErrorResponse other) { + error(other.getError()); + return this; + } + + @java.lang.Override + @JsonSetter("error") + public _FinalStage error(@NotNull ErrorResponseError error) { + this.error = Objects.requireNonNull(error, "error must not be null"); + return this; + } + + @java.lang.Override + public ErrorResponse build() { + return new ErrorResponse(error, additionalProperties); + } + + @java.lang.Override + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + @java.lang.Override + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/ErrorResponseError.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/ErrorResponseError.java new file mode 100644 index 00000000..5c43d6a6 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/ErrorResponseError.java @@ -0,0 +1,236 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import org.jetbrains.annotations.NotNull; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = ErrorResponseError.Builder.class) +public final class ErrorResponseError { + private final int grpcCode; + + private final int httpCode; + + private final String httpStatus; + + private final String message; + + private final Optional>> details; + + private final Map additionalProperties; + + private ErrorResponseError( + int grpcCode, + int httpCode, + String httpStatus, + String message, + Optional>> details, + Map additionalProperties) { + this.grpcCode = grpcCode; + this.httpCode = httpCode; + this.httpStatus = httpStatus; + this.message = message; + this.details = details; + this.additionalProperties = additionalProperties; + } + + /** + * @return gRPC status codes. See https://grpc.io/docs/guides/status-codes. + */ + @JsonProperty("grpc_code") + public int getGrpcCode() { + return grpcCode; + } + + @JsonProperty("http_code") + public int getHttpCode() { + return httpCode; + } + + @JsonProperty("http_status") + public String getHttpStatus() { + return httpStatus; + } + + @JsonProperty("message") + public String getMessage() { + return message; + } + + @JsonProperty("details") + public Optional>> getDetails() { + return details; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof ErrorResponseError && equalTo((ErrorResponseError) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(ErrorResponseError other) { + return grpcCode == other.grpcCode + && httpCode == other.httpCode + && httpStatus.equals(other.httpStatus) + && message.equals(other.message) + && details.equals(other.details); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.grpcCode, this.httpCode, this.httpStatus, this.message, this.details); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static GrpcCodeStage builder() { + return new Builder(); + } + + public interface GrpcCodeStage { + /** + *

gRPC status codes. See https://grpc.io/docs/guides/status-codes.

+ */ + HttpCodeStage grpcCode(int grpcCode); + + Builder from(ErrorResponseError other); + } + + public interface HttpCodeStage { + HttpStatusStage httpCode(int httpCode); + } + + public interface HttpStatusStage { + MessageStage httpStatus(@NotNull String httpStatus); + } + + public interface MessageStage { + _FinalStage message(@NotNull String message); + } + + public interface _FinalStage { + ErrorResponseError build(); + + _FinalStage additionalProperty(String key, Object value); + + _FinalStage additionalProperties(Map additionalProperties); + + _FinalStage details(Optional>> details); + + _FinalStage details(List> details); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder + implements GrpcCodeStage, HttpCodeStage, HttpStatusStage, MessageStage, _FinalStage { + private int grpcCode; + + private int httpCode; + + private String httpStatus; + + private String message; + + private Optional>> details = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + @java.lang.Override + public Builder from(ErrorResponseError other) { + grpcCode(other.getGrpcCode()); + httpCode(other.getHttpCode()); + httpStatus(other.getHttpStatus()); + message(other.getMessage()); + details(other.getDetails()); + return this; + } + + /** + *

gRPC status codes. See https://grpc.io/docs/guides/status-codes.

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonSetter("grpc_code") + public HttpCodeStage grpcCode(int grpcCode) { + this.grpcCode = grpcCode; + return this; + } + + @java.lang.Override + @JsonSetter("http_code") + public HttpStatusStage httpCode(int httpCode) { + this.httpCode = httpCode; + return this; + } + + @java.lang.Override + @JsonSetter("http_status") + public MessageStage httpStatus(@NotNull String httpStatus) { + this.httpStatus = Objects.requireNonNull(httpStatus, "httpStatus must not be null"); + return this; + } + + @java.lang.Override + @JsonSetter("message") + public _FinalStage message(@NotNull String message) { + this.message = Objects.requireNonNull(message, "message must not be null"); + return this; + } + + @java.lang.Override + public _FinalStage details(List> details) { + this.details = Optional.ofNullable(details); + return this; + } + + @java.lang.Override + @JsonSetter(value = "details", nulls = Nulls.SKIP) + public _FinalStage details(Optional>> details) { + this.details = details; + return this; + } + + @java.lang.Override + public ErrorResponseError build() { + return new ErrorResponseError(grpcCode, httpCode, httpStatus, message, details, additionalProperties); + } + + @java.lang.Override + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + @java.lang.Override + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/FileMapping.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/FileMapping.java new file mode 100644 index 00000000..75b023be --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/FileMapping.java @@ -0,0 +1,244 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = FileMapping.Builder.class) +public final class FileMapping { + private final Optional source; + + private final Optional destination; + + private final Optional outputFileNameSuffix; + + private final Optional entitiesDestination; + + private final Optional objectEntitiesDestination; + + private final Map additionalProperties; + + private FileMapping( + Optional source, + Optional destination, + Optional outputFileNameSuffix, + Optional entitiesDestination, + Optional objectEntitiesDestination, + Map additionalProperties) { + this.source = source; + this.destination = destination; + this.outputFileNameSuffix = outputFileNameSuffix; + this.entitiesDestination = entitiesDestination; + this.objectEntitiesDestination = objectEntitiesDestination; + this.additionalProperties = additionalProperties; + } + + /** + * @return Location of the source file. + */ + @JsonProperty("source") + public Optional getSource() { + return source; + } + + /** + * @return Location of the target file. + */ + @JsonProperty("destination") + public Optional getDestination() { + return destination; + } + + /** + * @return Suffix of the output file. For example, with outputFileNameSuffix = "_deidentified", "invoice.pdf" becomes "invoice_deidentified.pdf". + */ + @JsonProperty("outputFileNameSuffix") + public Optional getOutputFileNameSuffix() { + return outputFileNameSuffix; + } + + /** + * @return Location of the entities output, as "table.column". For example, "documents.entities_json". Must be in the same table as source. If unset, the entities output is not stored in the vault. + */ + @JsonProperty("entitiesDestination") + public Optional getEntitiesDestination() { + return entitiesDestination; + } + + /** + * @return Location of the object entities output, as "table.column". For example, "documents.object_entities_json". Must be in the same table as source. If unset, the object entities output is not stored in the vault. + */ + @JsonProperty("objectEntitiesDestination") + public Optional getObjectEntitiesDestination() { + return objectEntitiesDestination; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof FileMapping && equalTo((FileMapping) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(FileMapping other) { + return source.equals(other.source) + && destination.equals(other.destination) + && outputFileNameSuffix.equals(other.outputFileNameSuffix) + && entitiesDestination.equals(other.entitiesDestination) + && objectEntitiesDestination.equals(other.objectEntitiesDestination); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash( + this.source, + this.destination, + this.outputFileNameSuffix, + this.entitiesDestination, + this.objectEntitiesDestination); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + private Optional source = Optional.empty(); + + private Optional destination = Optional.empty(); + + private Optional outputFileNameSuffix = Optional.empty(); + + private Optional entitiesDestination = Optional.empty(); + + private Optional objectEntitiesDestination = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(FileMapping other) { + source(other.getSource()); + destination(other.getDestination()); + outputFileNameSuffix(other.getOutputFileNameSuffix()); + entitiesDestination(other.getEntitiesDestination()); + objectEntitiesDestination(other.getObjectEntitiesDestination()); + return this; + } + + /** + *

Location of the source file.

+ */ + @JsonSetter(value = "source", nulls = Nulls.SKIP) + public Builder source(Optional source) { + this.source = source; + return this; + } + + public Builder source(String source) { + this.source = Optional.ofNullable(source); + return this; + } + + /** + *

Location of the target file.

+ */ + @JsonSetter(value = "destination", nulls = Nulls.SKIP) + public Builder destination(Optional destination) { + this.destination = destination; + return this; + } + + public Builder destination(String destination) { + this.destination = Optional.ofNullable(destination); + return this; + } + + /** + *

Suffix of the output file. For example, with outputFileNameSuffix = "_deidentified", "invoice.pdf" becomes "invoice_deidentified.pdf".

+ */ + @JsonSetter(value = "outputFileNameSuffix", nulls = Nulls.SKIP) + public Builder outputFileNameSuffix(Optional outputFileNameSuffix) { + this.outputFileNameSuffix = outputFileNameSuffix; + return this; + } + + public Builder outputFileNameSuffix(String outputFileNameSuffix) { + this.outputFileNameSuffix = Optional.ofNullable(outputFileNameSuffix); + return this; + } + + /** + *

Location of the entities output, as "table.column". For example, "documents.entities_json". Must be in the same table as source. If unset, the entities output is not stored in the vault.

+ */ + @JsonSetter(value = "entitiesDestination", nulls = Nulls.SKIP) + public Builder entitiesDestination(Optional entitiesDestination) { + this.entitiesDestination = entitiesDestination; + return this; + } + + public Builder entitiesDestination(String entitiesDestination) { + this.entitiesDestination = Optional.ofNullable(entitiesDestination); + return this; + } + + /** + *

Location of the object entities output, as "table.column". For example, "documents.object_entities_json". Must be in the same table as source. If unset, the object entities output is not stored in the vault.

+ */ + @JsonSetter(value = "objectEntitiesDestination", nulls = Nulls.SKIP) + public Builder objectEntitiesDestination(Optional objectEntitiesDestination) { + this.objectEntitiesDestination = objectEntitiesDestination; + return this; + } + + public Builder objectEntitiesDestination(String objectEntitiesDestination) { + this.objectEntitiesDestination = Optional.ofNullable(objectEntitiesDestination); + return this; + } + + public FileMapping build() { + return new FileMapping( + source, + destination, + outputFileNameSuffix, + entitiesDestination, + objectEntitiesDestination, + additionalProperties); + } + + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/FileOutput.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/FileOutput.java new file mode 100644 index 00000000..69f6cdf8 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/FileOutput.java @@ -0,0 +1,173 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = FileOutput.Builder.class) +public final class FileOutput { + private final Optional processedFile; + + private final Optional processedFileType; + + private final Optional processedFileExtension; + + private final Map additionalProperties; + + private FileOutput( + Optional processedFile, + Optional processedFileType, + Optional processedFileExtension, + Map additionalProperties) { + this.processedFile = processedFile; + this.processedFileType = processedFileType; + this.processedFileExtension = processedFileExtension; + this.additionalProperties = additionalProperties; + } + + /** + * @return Value of the processed file. + */ + @JsonProperty("processedFile") + public Optional getProcessedFile() { + return processedFile; + } + + /** + * @return Type of the processed file. + */ + @JsonProperty("processedFileType") + public Optional getProcessedFileType() { + return processedFileType; + } + + /** + * @return Processed file extension. + */ + @JsonProperty("processedFileExtension") + public Optional getProcessedFileExtension() { + return processedFileExtension; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof FileOutput && equalTo((FileOutput) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(FileOutput other) { + return processedFile.equals(other.processedFile) + && processedFileType.equals(other.processedFileType) + && processedFileExtension.equals(other.processedFileExtension); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.processedFile, this.processedFileType, this.processedFileExtension); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + private Optional processedFile = Optional.empty(); + + private Optional processedFileType = Optional.empty(); + + private Optional processedFileExtension = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(FileOutput other) { + processedFile(other.getProcessedFile()); + processedFileType(other.getProcessedFileType()); + processedFileExtension(other.getProcessedFileExtension()); + return this; + } + + /** + *

Value of the processed file.

+ */ + @JsonSetter(value = "processedFile", nulls = Nulls.SKIP) + public Builder processedFile(Optional processedFile) { + this.processedFile = processedFile; + return this; + } + + public Builder processedFile(String processedFile) { + this.processedFile = Optional.ofNullable(processedFile); + return this; + } + + /** + *

Type of the processed file.

+ */ + @JsonSetter(value = "processedFileType", nulls = Nulls.SKIP) + public Builder processedFileType(Optional processedFileType) { + this.processedFileType = processedFileType; + return this; + } + + public Builder processedFileType(FileOutputProcessedFileType processedFileType) { + this.processedFileType = Optional.ofNullable(processedFileType); + return this; + } + + /** + *

Processed file extension.

+ */ + @JsonSetter(value = "processedFileExtension", nulls = Nulls.SKIP) + public Builder processedFileExtension(Optional processedFileExtension) { + this.processedFileExtension = processedFileExtension; + return this; + } + + public Builder processedFileExtension(FileOutputProcessedFileExtension processedFileExtension) { + this.processedFileExtension = Optional.ofNullable(processedFileExtension); + return this; + } + + public FileOutput build() { + return new FileOutput(processedFile, processedFileType, processedFileExtension, additionalProperties); + } + + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/FileOutputProcessedFileExtension.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/FileOutputProcessedFileExtension.java new file mode 100644 index 00000000..5eb8fd6f --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/FileOutputProcessedFileExtension.java @@ -0,0 +1,301 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +public final class FileOutputProcessedFileExtension { + public static final FileOutputProcessedFileExtension BMP = new FileOutputProcessedFileExtension(Value.BMP, "bmp"); + + public static final FileOutputProcessedFileExtension PPTX = + new FileOutputProcessedFileExtension(Value.PPTX, "pptx"); + + public static final FileOutputProcessedFileExtension WAV = new FileOutputProcessedFileExtension(Value.WAV, "wav"); + + public static final FileOutputProcessedFileExtension DOC = new FileOutputProcessedFileExtension(Value.DOC, "doc"); + + public static final FileOutputProcessedFileExtension XML = new FileOutputProcessedFileExtension(Value.XML, "xml"); + + public static final FileOutputProcessedFileExtension ZIP = new FileOutputProcessedFileExtension(Value.ZIP, "zip"); + + public static final FileOutputProcessedFileExtension MP3 = new FileOutputProcessedFileExtension(Value.MP3, "mp3"); + + public static final FileOutputProcessedFileExtension PPT = new FileOutputProcessedFileExtension(Value.PPT, "ppt"); + + public static final FileOutputProcessedFileExtension JPEG = + new FileOutputProcessedFileExtension(Value.JPEG, "jpeg"); + + public static final FileOutputProcessedFileExtension TIFF = + new FileOutputProcessedFileExtension(Value.TIFF, "tiff"); + + public static final FileOutputProcessedFileExtension PDF = new FileOutputProcessedFileExtension(Value.PDF, "pdf"); + + public static final FileOutputProcessedFileExtension DOCX = + new FileOutputProcessedFileExtension(Value.DOCX, "docx"); + + public static final FileOutputProcessedFileExtension PNG = new FileOutputProcessedFileExtension(Value.PNG, "png"); + + public static final FileOutputProcessedFileExtension JSONL = + new FileOutputProcessedFileExtension(Value.JSONL, "jsonl"); + + public static final FileOutputProcessedFileExtension CSV = new FileOutputProcessedFileExtension(Value.CSV, "csv"); + + public static final FileOutputProcessedFileExtension JPG = new FileOutputProcessedFileExtension(Value.JPG, "jpg"); + + public static final FileOutputProcessedFileExtension DCM = new FileOutputProcessedFileExtension(Value.DCM, "dcm"); + + public static final FileOutputProcessedFileExtension JSON = + new FileOutputProcessedFileExtension(Value.JSON, "json"); + + public static final FileOutputProcessedFileExtension GIF = new FileOutputProcessedFileExtension(Value.GIF, "gif"); + + public static final FileOutputProcessedFileExtension TXT = new FileOutputProcessedFileExtension(Value.TXT, "txt"); + + public static final FileOutputProcessedFileExtension XLS = new FileOutputProcessedFileExtension(Value.XLS, "xls"); + + public static final FileOutputProcessedFileExtension XLSX = + new FileOutputProcessedFileExtension(Value.XLSX, "xlsx"); + + public static final FileOutputProcessedFileExtension TIF = new FileOutputProcessedFileExtension(Value.TIF, "tif"); + + private final Value value; + + private final String string; + + FileOutputProcessedFileExtension(Value value, String string) { + this.value = value; + this.string = string; + } + + public Value getEnumValue() { + return value; + } + + @java.lang.Override + @JsonValue + public String toString() { + return this.string; + } + + @java.lang.Override + public boolean equals(Object other) { + return (this == other) + || (other instanceof FileOutputProcessedFileExtension + && this.string.equals(((FileOutputProcessedFileExtension) other).string)); + } + + @java.lang.Override + public int hashCode() { + return this.string.hashCode(); + } + + public T visit(Visitor visitor) { + switch (value) { + case BMP: + return visitor.visitBmp(); + case PPTX: + return visitor.visitPptx(); + case WAV: + return visitor.visitWav(); + case DOC: + return visitor.visitDoc(); + case XML: + return visitor.visitXml(); + case ZIP: + return visitor.visitZip(); + case MP3: + return visitor.visitMp3(); + case PPT: + return visitor.visitPpt(); + case JPEG: + return visitor.visitJpeg(); + case TIFF: + return visitor.visitTiff(); + case PDF: + return visitor.visitPdf(); + case DOCX: + return visitor.visitDocx(); + case PNG: + return visitor.visitPng(); + case JSONL: + return visitor.visitJsonl(); + case CSV: + return visitor.visitCsv(); + case JPG: + return visitor.visitJpg(); + case DCM: + return visitor.visitDcm(); + case JSON: + return visitor.visitJson(); + case GIF: + return visitor.visitGif(); + case TXT: + return visitor.visitTxt(); + case XLS: + return visitor.visitXls(); + case XLSX: + return visitor.visitXlsx(); + case TIF: + return visitor.visitTif(); + case UNKNOWN: + default: + return visitor.visitUnknown(string); + } + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static FileOutputProcessedFileExtension valueOf(String value) { + switch (value) { + case "bmp": + return BMP; + case "pptx": + return PPTX; + case "wav": + return WAV; + case "doc": + return DOC; + case "xml": + return XML; + case "zip": + return ZIP; + case "mp3": + return MP3; + case "ppt": + return PPT; + case "jpeg": + return JPEG; + case "tiff": + return TIFF; + case "pdf": + return PDF; + case "docx": + return DOCX; + case "png": + return PNG; + case "jsonl": + return JSONL; + case "csv": + return CSV; + case "jpg": + return JPG; + case "dcm": + return DCM; + case "json": + return JSON; + case "gif": + return GIF; + case "txt": + return TXT; + case "xls": + return XLS; + case "xlsx": + return XLSX; + case "tif": + return TIF; + default: + return new FileOutputProcessedFileExtension(Value.UNKNOWN, value); + } + } + + public enum Value { + MP3, + + WAV, + + PDF, + + TXT, + + CSV, + + JSON, + + JPG, + + JPEG, + + TIF, + + TIFF, + + PNG, + + BMP, + + XLS, + + XLSX, + + DOC, + + DOCX, + + PPT, + + PPTX, + + XML, + + DCM, + + JSONL, + + ZIP, + + GIF, + + UNKNOWN + } + + public interface Visitor { + T visitMp3(); + + T visitWav(); + + T visitPdf(); + + T visitTxt(); + + T visitCsv(); + + T visitJson(); + + T visitJpg(); + + T visitJpeg(); + + T visitTif(); + + T visitTiff(); + + T visitPng(); + + T visitBmp(); + + T visitXls(); + + T visitXlsx(); + + T visitDoc(); + + T visitDocx(); + + T visitPpt(); + + T visitPptx(); + + T visitXml(); + + T visitDcm(); + + T visitJsonl(); + + T visitZip(); + + T visitGif(); + + T visitUnknown(String unknownType); + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/FileOutputProcessedFileType.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/FileOutputProcessedFileType.java new file mode 100644 index 00000000..faeb8849 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/FileOutputProcessedFileType.java @@ -0,0 +1,64 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +public final class FileOutputProcessedFileType { + private final Value value; + + private final String string; + + FileOutputProcessedFileType(Value value, String string) { + this.value = value; + this.string = string; + } + + public Value getEnumValue() { + return value; + } + + @java.lang.Override + @JsonValue + public String toString() { + return this.string; + } + + @java.lang.Override + public boolean equals(Object other) { + return (this == other) + || (other instanceof FileOutputProcessedFileType + && this.string.equals(((FileOutputProcessedFileType) other).string)); + } + + @java.lang.Override + public int hashCode() { + return this.string.hashCode(); + } + + public T visit(Visitor visitor) { + switch (value) { + case UNKNOWN: + default: + return visitor.visitUnknown(string); + } + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static FileOutputProcessedFileType valueOf(String value) { + switch (value) { + default: + return new FileOutputProcessedFileType(Value.UNKNOWN, value); + } + } + + public enum Value { + UNKNOWN + } + + public interface Visitor { + T visitUnknown(String unknownType); + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/ImageV2.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/ImageV2.java new file mode 100644 index 00000000..ebcc89de --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/ImageV2.java @@ -0,0 +1,173 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = ImageV2.Builder.class) +public final class ImageV2 { + private final Optional outputProcessedImage; + + private final Optional outputOcrText; + + private final Optional maskingMethod; + + private final Map additionalProperties; + + private ImageV2( + Optional outputProcessedImage, + Optional outputOcrText, + Optional maskingMethod, + Map additionalProperties) { + this.outputProcessedImage = outputProcessedImage; + this.outputOcrText = outputOcrText; + this.maskingMethod = maskingMethod; + this.additionalProperties = additionalProperties; + } + + /** + * @return If true, includes processed image in the output. + */ + @JsonProperty("outputProcessedImage") + public Optional getOutputProcessedImage() { + return outputProcessedImage; + } + + /** + * @return If true, includes text detected by OCR in the response. + */ + @JsonProperty("outputOcrText") + public Optional getOutputOcrText() { + return outputOcrText; + } + + /** + * @return Method used to mask detected entities in the image. BLUR: blurs each detected entity so the sensitive content is unreadable while the rest of the image is preserved. BLACKBOX: covers each detected entity with a solid black box. + */ + @JsonProperty("maskingMethod") + public Optional getMaskingMethod() { + return maskingMethod; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof ImageV2 && equalTo((ImageV2) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(ImageV2 other) { + return outputProcessedImage.equals(other.outputProcessedImage) + && outputOcrText.equals(other.outputOcrText) + && maskingMethod.equals(other.maskingMethod); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.outputProcessedImage, this.outputOcrText, this.maskingMethod); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + private Optional outputProcessedImage = Optional.empty(); + + private Optional outputOcrText = Optional.empty(); + + private Optional maskingMethod = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(ImageV2 other) { + outputProcessedImage(other.getOutputProcessedImage()); + outputOcrText(other.getOutputOcrText()); + maskingMethod(other.getMaskingMethod()); + return this; + } + + /** + *

If true, includes processed image in the output.

+ */ + @JsonSetter(value = "outputProcessedImage", nulls = Nulls.SKIP) + public Builder outputProcessedImage(Optional outputProcessedImage) { + this.outputProcessedImage = outputProcessedImage; + return this; + } + + public Builder outputProcessedImage(Boolean outputProcessedImage) { + this.outputProcessedImage = Optional.ofNullable(outputProcessedImage); + return this; + } + + /** + *

If true, includes text detected by OCR in the response.

+ */ + @JsonSetter(value = "outputOcrText", nulls = Nulls.SKIP) + public Builder outputOcrText(Optional outputOcrText) { + this.outputOcrText = outputOcrText; + return this; + } + + public Builder outputOcrText(Boolean outputOcrText) { + this.outputOcrText = Optional.ofNullable(outputOcrText); + return this; + } + + /** + *

Method used to mask detected entities in the image. BLUR: blurs each detected entity so the sensitive content is unreadable while the rest of the image is preserved. BLACKBOX: covers each detected entity with a solid black box.

+ */ + @JsonSetter(value = "maskingMethod", nulls = Nulls.SKIP) + public Builder maskingMethod(Optional maskingMethod) { + this.maskingMethod = maskingMethod; + return this; + } + + public Builder maskingMethod(ImageV2MaskingMethod maskingMethod) { + this.maskingMethod = Optional.ofNullable(maskingMethod); + return this; + } + + public ImageV2 build() { + return new ImageV2(outputProcessedImage, outputOcrText, maskingMethod, additionalProperties); + } + + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/ImageV2MaskingMethod.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/ImageV2MaskingMethod.java new file mode 100644 index 00000000..41f59512 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/ImageV2MaskingMethod.java @@ -0,0 +1,93 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +public final class ImageV2MaskingMethod { + public static final ImageV2MaskingMethod BLUR = new ImageV2MaskingMethod(Value.BLUR, "BLUR"); + + public static final ImageV2MaskingMethod NONE = new ImageV2MaskingMethod(Value.NONE, "NONE"); + + public static final ImageV2MaskingMethod BLACKBOX = new ImageV2MaskingMethod(Value.BLACKBOX, "BLACKBOX"); + + private final Value value; + + private final String string; + + ImageV2MaskingMethod(Value value, String string) { + this.value = value; + this.string = string; + } + + public Value getEnumValue() { + return value; + } + + @java.lang.Override + @JsonValue + public String toString() { + return this.string; + } + + @java.lang.Override + public boolean equals(Object other) { + return (this == other) + || (other instanceof ImageV2MaskingMethod && this.string.equals(((ImageV2MaskingMethod) other).string)); + } + + @java.lang.Override + public int hashCode() { + return this.string.hashCode(); + } + + public T visit(Visitor visitor) { + switch (value) { + case BLUR: + return visitor.visitBlur(); + case NONE: + return visitor.visitNone(); + case BLACKBOX: + return visitor.visitBlackbox(); + case UNKNOWN: + default: + return visitor.visitUnknown(string); + } + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static ImageV2MaskingMethod valueOf(String value) { + switch (value) { + case "BLUR": + return BLUR; + case "NONE": + return NONE; + case "BLACKBOX": + return BLACKBOX; + default: + return new ImageV2MaskingMethod(Value.UNKNOWN, value); + } + } + + public enum Value { + NONE, + + BLUR, + + BLACKBOX, + + UNKNOWN + } + + public interface Visitor { + T visitNone(); + + T visitBlur(); + + T visitBlackbox(); + + T visitUnknown(String unknownType); + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/Media.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/Media.java new file mode 100644 index 00000000..7bba13bb --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/Media.java @@ -0,0 +1,153 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = Media.Builder.class) +public final class Media { + private final Optional audio; + + private final Optional document; + + private final Optional image; + + private final Map additionalProperties; + + private Media( + Optional audio, + Optional document, + Optional image, + Map additionalProperties) { + this.audio = audio; + this.document = document; + this.image = image; + this.additionalProperties = additionalProperties; + } + + @JsonProperty("audio") + public Optional getAudio() { + return audio; + } + + @JsonProperty("document") + public Optional getDocument() { + return document; + } + + @JsonProperty("image") + public Optional getImage() { + return image; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof Media && equalTo((Media) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(Media other) { + return audio.equals(other.audio) && document.equals(other.document) && image.equals(other.image); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.audio, this.document, this.image); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + private Optional audio = Optional.empty(); + + private Optional document = Optional.empty(); + + private Optional image = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(Media other) { + audio(other.getAudio()); + document(other.getDocument()); + image(other.getImage()); + return this; + } + + @JsonSetter(value = "audio", nulls = Nulls.SKIP) + public Builder audio(Optional audio) { + this.audio = audio; + return this; + } + + public Builder audio(AudioV2 audio) { + this.audio = Optional.ofNullable(audio); + return this; + } + + @JsonSetter(value = "document", nulls = Nulls.SKIP) + public Builder document(Optional document) { + this.document = document; + return this; + } + + public Builder document(DocumentV2 document) { + this.document = Optional.ofNullable(document); + return this; + } + + @JsonSetter(value = "image", nulls = Nulls.SKIP) + public Builder image(Optional image) { + this.image = image; + return this; + } + + public Builder image(ImageV2 image) { + this.image = Optional.ofNullable(image); + return this; + } + + public Media build() { + return new Media(audio, document, image, additionalProperties); + } + + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/Metrics.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/Metrics.java new file mode 100644 index 00000000..ac525333 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/Metrics.java @@ -0,0 +1,263 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = Metrics.Builder.class) +public final class Metrics { + private final Optional size; + + private final Optional wordCount; + + private final Optional characterCount; + + private final Optional slides; + + private final Optional pages; + + private final Optional duration; + + private final Map additionalProperties; + + private Metrics( + Optional size, + Optional wordCount, + Optional characterCount, + Optional slides, + Optional pages, + Optional duration, + Map additionalProperties) { + this.size = size; + this.wordCount = wordCount; + this.characterCount = characterCount; + this.slides = slides; + this.pages = pages; + this.duration = duration; + this.additionalProperties = additionalProperties; + } + + /** + * @return Size of the input text(KB). + */ + @JsonProperty("size") + public Optional getSize() { + return size; + } + + /** + * @return Total number of words processed. + */ + @JsonProperty("wordCount") + public Optional getWordCount() { + return wordCount; + } + + /** + * @return Total number of characters processed. + */ + @JsonProperty("characterCount") + public Optional getCharacterCount() { + return characterCount; + } + + /** + * @return Total number of slides processed. + */ + @JsonProperty("slides") + public Optional getSlides() { + return slides; + } + + /** + * @return Total number of pages processed. + */ + @JsonProperty("pages") + public Optional getPages() { + return pages; + } + + /** + * @return Total duration processed(seconds). + */ + @JsonProperty("duration") + public Optional getDuration() { + return duration; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof Metrics && equalTo((Metrics) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(Metrics other) { + return size.equals(other.size) + && wordCount.equals(other.wordCount) + && characterCount.equals(other.characterCount) + && slides.equals(other.slides) + && pages.equals(other.pages) + && duration.equals(other.duration); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.size, this.wordCount, this.characterCount, this.slides, this.pages, this.duration); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + private Optional size = Optional.empty(); + + private Optional wordCount = Optional.empty(); + + private Optional characterCount = Optional.empty(); + + private Optional slides = Optional.empty(); + + private Optional pages = Optional.empty(); + + private Optional duration = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(Metrics other) { + size(other.getSize()); + wordCount(other.getWordCount()); + characterCount(other.getCharacterCount()); + slides(other.getSlides()); + pages(other.getPages()); + duration(other.getDuration()); + return this; + } + + /** + *

Size of the input text(KB).

+ */ + @JsonSetter(value = "size", nulls = Nulls.SKIP) + public Builder size(Optional size) { + this.size = size; + return this; + } + + public Builder size(Double size) { + this.size = Optional.ofNullable(size); + return this; + } + + /** + *

Total number of words processed.

+ */ + @JsonSetter(value = "wordCount", nulls = Nulls.SKIP) + public Builder wordCount(Optional wordCount) { + this.wordCount = wordCount; + return this; + } + + public Builder wordCount(Integer wordCount) { + this.wordCount = Optional.ofNullable(wordCount); + return this; + } + + /** + *

Total number of characters processed.

+ */ + @JsonSetter(value = "characterCount", nulls = Nulls.SKIP) + public Builder characterCount(Optional characterCount) { + this.characterCount = characterCount; + return this; + } + + public Builder characterCount(Integer characterCount) { + this.characterCount = Optional.ofNullable(characterCount); + return this; + } + + /** + *

Total number of slides processed.

+ */ + @JsonSetter(value = "slides", nulls = Nulls.SKIP) + public Builder slides(Optional slides) { + this.slides = slides; + return this; + } + + public Builder slides(Integer slides) { + this.slides = Optional.ofNullable(slides); + return this; + } + + /** + *

Total number of pages processed.

+ */ + @JsonSetter(value = "pages", nulls = Nulls.SKIP) + public Builder pages(Optional pages) { + this.pages = pages; + return this; + } + + public Builder pages(Integer pages) { + this.pages = Optional.ofNullable(pages); + return this; + } + + /** + *

Total duration processed(seconds).

+ */ + @JsonSetter(value = "duration", nulls = Nulls.SKIP) + public Builder duration(Optional duration) { + this.duration = duration; + return this; + } + + public Builder duration(Double duration) { + this.duration = Optional.ofNullable(duration); + return this; + } + + public Metrics build() { + return new Metrics(size, wordCount, characterCount, slides, pages, duration, additionalProperties); + } + + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/RedactionLevel.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/RedactionLevel.java new file mode 100644 index 00000000..961c2f2f --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/RedactionLevel.java @@ -0,0 +1,204 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = RedactionLevel.Builder.class) +public final class RedactionLevel { + private final Optional tokenGroupName; + + private final Optional entityName; + + private final Optional redactionType; + + private final Optional redactionPattern; + + private final Map additionalProperties; + + private RedactionLevel( + Optional tokenGroupName, + Optional entityName, + Optional redactionType, + Optional redactionPattern, + Map additionalProperties) { + this.tokenGroupName = tokenGroupName; + this.entityName = entityName; + this.redactionType = redactionType; + this.redactionPattern = redactionPattern; + this.additionalProperties = additionalProperties; + } + + /** + * @return Name of the token group whose detokenized values should be masked (e.g., a token in personTokenGroup that detokenizes to John Doe is masked as JoXXXe). Use this for FlowVault. + */ + @JsonProperty("tokenGroupName") + public Optional getTokenGroupName() { + return tokenGroupName; + } + + /** + * @return Entity type whose detokenized values should be shown in plain text, redacted, or masked based on redactionType (e.g., a NAME entity that detokenizes to John Doe is masked as JoXXXe). Use this for SkyVault. + */ + @JsonProperty("entityName") + public Optional getEntityName() { + return entityName; + } + + /** + * @return Redaction method to apply to the detokenized value (e.g., for the value John Doe, MASKED transforms it into JoXXXe, REDACTED transforms it into *REDACTED*, and PLAIN_TEXT keeps it as John Doe). Use this for SkyVault. + */ + @JsonProperty("redactionType") + public Optional getRedactionType() { + return redactionType; + } + + /** + * @return Redaction method to apply to the detokenized value. Use the name of a redaction pattern configured in the vault (e.g., personMaskPattern transforms John Doe into JoXXXe). Use this for FlowVault. + */ + @JsonProperty("redactionPattern") + public Optional getRedactionPattern() { + return redactionPattern; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof RedactionLevel && equalTo((RedactionLevel) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(RedactionLevel other) { + return tokenGroupName.equals(other.tokenGroupName) + && entityName.equals(other.entityName) + && redactionType.equals(other.redactionType) + && redactionPattern.equals(other.redactionPattern); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.tokenGroupName, this.entityName, this.redactionType, this.redactionPattern); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + private Optional tokenGroupName = Optional.empty(); + + private Optional entityName = Optional.empty(); + + private Optional redactionType = Optional.empty(); + + private Optional redactionPattern = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(RedactionLevel other) { + tokenGroupName(other.getTokenGroupName()); + entityName(other.getEntityName()); + redactionType(other.getRedactionType()); + redactionPattern(other.getRedactionPattern()); + return this; + } + + /** + *

Name of the token group whose detokenized values should be masked (e.g., a token in personTokenGroup that detokenizes to John Doe is masked as JoXXXe). Use this for FlowVault.

+ */ + @JsonSetter(value = "tokenGroupName", nulls = Nulls.SKIP) + public Builder tokenGroupName(Optional tokenGroupName) { + this.tokenGroupName = tokenGroupName; + return this; + } + + public Builder tokenGroupName(String tokenGroupName) { + this.tokenGroupName = Optional.ofNullable(tokenGroupName); + return this; + } + + /** + *

Entity type whose detokenized values should be shown in plain text, redacted, or masked based on redactionType (e.g., a NAME entity that detokenizes to John Doe is masked as JoXXXe). Use this for SkyVault.

+ */ + @JsonSetter(value = "entityName", nulls = Nulls.SKIP) + public Builder entityName(Optional entityName) { + this.entityName = entityName; + return this; + } + + public Builder entityName(RedactionLevelEntityName entityName) { + this.entityName = Optional.ofNullable(entityName); + return this; + } + + /** + *

Redaction method to apply to the detokenized value (e.g., for the value John Doe, MASKED transforms it into JoXXXe, REDACTED transforms it into *REDACTED*, and PLAIN_TEXT keeps it as John Doe). Use this for SkyVault.

+ */ + @JsonSetter(value = "redactionType", nulls = Nulls.SKIP) + public Builder redactionType(Optional redactionType) { + this.redactionType = redactionType; + return this; + } + + public Builder redactionType(RedactionLevelRedactionType redactionType) { + this.redactionType = Optional.ofNullable(redactionType); + return this; + } + + /** + *

Redaction method to apply to the detokenized value. Use the name of a redaction pattern configured in the vault (e.g., personMaskPattern transforms John Doe into JoXXXe). Use this for FlowVault.

+ */ + @JsonSetter(value = "redactionPattern", nulls = Nulls.SKIP) + public Builder redactionPattern(Optional redactionPattern) { + this.redactionPattern = redactionPattern; + return this; + } + + public Builder redactionPattern(String redactionPattern) { + this.redactionPattern = Optional.ofNullable(redactionPattern); + return this; + } + + public RedactionLevel build() { + return new RedactionLevel( + tokenGroupName, entityName, redactionType, redactionPattern, additionalProperties); + } + + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/RedactionLevelEntityName.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/RedactionLevelEntityName.java new file mode 100644 index 00000000..a4e9acb6 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/RedactionLevelEntityName.java @@ -0,0 +1,812 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +public final class RedactionLevelEntityName { + public static final RedactionLevelEntityName PHONE_NUMBER = + new RedactionLevelEntityName(Value.PHONE_NUMBER, "PHONE_NUMBER"); + + public static final RedactionLevelEntityName USERNAME = new RedactionLevelEntityName(Value.USERNAME, "USERNAME"); + + public static final RedactionLevelEntityName VEHICLE_ID = + new RedactionLevelEntityName(Value.VEHICLE_ID, "VEHICLE_ID"); + + public static final RedactionLevelEntityName RELIGION = new RedactionLevelEntityName(Value.RELIGION, "RELIGION"); + + public static final RedactionLevelEntityName ORIGIN = new RedactionLevelEntityName(Value.ORIGIN, "ORIGIN"); + + public static final RedactionLevelEntityName PASSPORT_NUMBER = + new RedactionLevelEntityName(Value.PASSPORT_NUMBER, "PASSPORT_NUMBER"); + + public static final RedactionLevelEntityName YEAR = new RedactionLevelEntityName(Value.YEAR, "YEAR"); + + public static final RedactionLevelEntityName CONDITION = new RedactionLevelEntityName(Value.CONDITION, "CONDITION"); + + public static final RedactionLevelEntityName LOCATION = new RedactionLevelEntityName(Value.LOCATION, "LOCATION"); + + public static final RedactionLevelEntityName LOCATION_CITY = + new RedactionLevelEntityName(Value.LOCATION_CITY, "LOCATION_CITY"); + + public static final RedactionLevelEntityName LOCATION_COUNTRY = + new RedactionLevelEntityName(Value.LOCATION_COUNTRY, "LOCATION_COUNTRY"); + + public static final RedactionLevelEntityName PROJECT = new RedactionLevelEntityName(Value.PROJECT, "PROJECT"); + + public static final RedactionLevelEntityName LOCATION_ZIP = + new RedactionLevelEntityName(Value.LOCATION_ZIP, "LOCATION_ZIP"); + + public static final RedactionLevelEntityName NAME_MEDICAL_PROFESSIONAL = + new RedactionLevelEntityName(Value.NAME_MEDICAL_PROFESSIONAL, "NAME_MEDICAL_PROFESSIONAL"); + + public static final RedactionLevelEntityName BANK_ACCOUNT = + new RedactionLevelEntityName(Value.BANK_ACCOUNT, "BANK_ACCOUNT"); + + public static final RedactionLevelEntityName OCCUPATION = + new RedactionLevelEntityName(Value.OCCUPATION, "OCCUPATION"); + + public static final RedactionLevelEntityName TIME = new RedactionLevelEntityName(Value.TIME, "TIME"); + + public static final RedactionLevelEntityName LOCATION_STATE = + new RedactionLevelEntityName(Value.LOCATION_STATE, "LOCATION_STATE"); + + public static final RedactionLevelEntityName EVENT = new RedactionLevelEntityName(Value.EVENT, "EVENT"); + + public static final RedactionLevelEntityName POLITICAL_AFFILIATION = + new RedactionLevelEntityName(Value.POLITICAL_AFFILIATION, "POLITICAL_AFFILIATION"); + + public static final RedactionLevelEntityName LANGUAGE = new RedactionLevelEntityName(Value.LANGUAGE, "LANGUAGE"); + + public static final RedactionLevelEntityName ZODIAC_SIGN = + new RedactionLevelEntityName(Value.ZODIAC_SIGN, "ZODIAC_SIGN"); + + public static final RedactionLevelEntityName PRODUCT = new RedactionLevelEntityName(Value.PRODUCT, "PRODUCT"); + + public static final RedactionLevelEntityName SEXUALITY = new RedactionLevelEntityName(Value.SEXUALITY, "SEXUALITY"); + + public static final RedactionLevelEntityName DURATION = new RedactionLevelEntityName(Value.DURATION, "DURATION"); + + public static final RedactionLevelEntityName PHYSICAL_ATTRIBUTE = + new RedactionLevelEntityName(Value.PHYSICAL_ATTRIBUTE, "PHYSICAL_ATTRIBUTE"); + + public static final RedactionLevelEntityName LOCATION_ADDRESS_STREET = + new RedactionLevelEntityName(Value.LOCATION_ADDRESS_STREET, "LOCATION_ADDRESS_STREET"); + + public static final RedactionLevelEntityName MEDICAL_PROCESS = + new RedactionLevelEntityName(Value.MEDICAL_PROCESS, "MEDICAL_PROCESS"); + + public static final RedactionLevelEntityName LOCATION_ADDRESS = + new RedactionLevelEntityName(Value.LOCATION_ADDRESS, "LOCATION_ADDRESS"); + + public static final RedactionLevelEntityName FILENAME = new RedactionLevelEntityName(Value.FILENAME, "FILENAME"); + + public static final RedactionLevelEntityName DOB = new RedactionLevelEntityName(Value.DOB, "DOB"); + + public static final RedactionLevelEntityName ORGANIZATION_MEDICAL_FACILITY = + new RedactionLevelEntityName(Value.ORGANIZATION_MEDICAL_FACILITY, "ORGANIZATION_MEDICAL_FACILITY"); + + public static final RedactionLevelEntityName ROUTING_NUMBER = + new RedactionLevelEntityName(Value.ROUTING_NUMBER, "ROUTING_NUMBER"); + + public static final RedactionLevelEntityName HEALTHCARE_NUMBER = + new RedactionLevelEntityName(Value.HEALTHCARE_NUMBER, "HEALTHCARE_NUMBER"); + + public static final RedactionLevelEntityName GENDER = new RedactionLevelEntityName(Value.GENDER, "GENDER"); + + public static final RedactionLevelEntityName NAME = new RedactionLevelEntityName(Value.NAME, "NAME"); + + public static final RedactionLevelEntityName DRUG = new RedactionLevelEntityName(Value.DRUG, "DRUG"); + + public static final RedactionLevelEntityName NONE = new RedactionLevelEntityName(Value.NONE, "NONE"); + + public static final RedactionLevelEntityName DATE_INTERVAL = + new RedactionLevelEntityName(Value.DATE_INTERVAL, "DATE_INTERVAL"); + + public static final RedactionLevelEntityName MARITAL_STATUS = + new RedactionLevelEntityName(Value.MARITAL_STATUS, "MARITAL_STATUS"); + + public static final RedactionLevelEntityName CORPORATE_ACTION = + new RedactionLevelEntityName(Value.CORPORATE_ACTION, "CORPORATE_ACTION"); + + public static final RedactionLevelEntityName AGE = new RedactionLevelEntityName(Value.AGE, "AGE"); + + public static final RedactionLevelEntityName CVV = new RedactionLevelEntityName(Value.CVV, "CVV"); + + public static final RedactionLevelEntityName NAME_GIVEN = + new RedactionLevelEntityName(Value.NAME_GIVEN, "NAME_GIVEN"); + + public static final RedactionLevelEntityName DATE = new RedactionLevelEntityName(Value.DATE, "DATE"); + + public static final RedactionLevelEntityName DAY = new RedactionLevelEntityName(Value.DAY, "DAY"); + + public static final RedactionLevelEntityName EFFECT = new RedactionLevelEntityName(Value.EFFECT, "EFFECT"); + + public static final RedactionLevelEntityName MEDICAL_CODE = + new RedactionLevelEntityName(Value.MEDICAL_CODE, "MEDICAL_CODE"); + + public static final RedactionLevelEntityName NUMERICAL_PII = + new RedactionLevelEntityName(Value.NUMERICAL_PII, "NUMERICAL_PII"); + + public static final RedactionLevelEntityName TREND = new RedactionLevelEntityName(Value.TREND, "TREND"); + + public static final RedactionLevelEntityName INJURY = new RedactionLevelEntityName(Value.INJURY, "INJURY"); + + public static final RedactionLevelEntityName DRIVER_LICENSE = + new RedactionLevelEntityName(Value.DRIVER_LICENSE, "DRIVER_LICENSE"); + + public static final RedactionLevelEntityName ALL = new RedactionLevelEntityName(Value.ALL, "ALL"); + + public static final RedactionLevelEntityName ORGANIZATION_ID = + new RedactionLevelEntityName(Value.ORGANIZATION_ID, "ORGANIZATION_ID"); + + public static final RedactionLevelEntityName PASSWORD = new RedactionLevelEntityName(Value.PASSWORD, "PASSWORD"); + + public static final RedactionLevelEntityName DOSE = new RedactionLevelEntityName(Value.DOSE, "DOSE"); + + public static final RedactionLevelEntityName NAME_FAMILY = + new RedactionLevelEntityName(Value.NAME_FAMILY, "NAME_FAMILY"); + + public static final RedactionLevelEntityName MONEY = new RedactionLevelEntityName(Value.MONEY, "MONEY"); + + public static final RedactionLevelEntityName URL = new RedactionLevelEntityName(Value.URL, "URL"); + + public static final RedactionLevelEntityName LOCATION_COORDINATE = + new RedactionLevelEntityName(Value.LOCATION_COORDINATE, "LOCATION_COORDINATE"); + + public static final RedactionLevelEntityName FINANCIAL_METRIC = + new RedactionLevelEntityName(Value.FINANCIAL_METRIC, "FINANCIAL_METRIC"); + + public static final RedactionLevelEntityName ORGANIZATION = + new RedactionLevelEntityName(Value.ORGANIZATION, "ORGANIZATION"); + + public static final RedactionLevelEntityName IP_ADDRESS = + new RedactionLevelEntityName(Value.IP_ADDRESS, "IP_ADDRESS"); + + public static final RedactionLevelEntityName BLOOD_TYPE = + new RedactionLevelEntityName(Value.BLOOD_TYPE, "BLOOD_TYPE"); + + public static final RedactionLevelEntityName MONTH = new RedactionLevelEntityName(Value.MONTH, "MONTH"); + + public static final RedactionLevelEntityName CREDIT_CARD = + new RedactionLevelEntityName(Value.CREDIT_CARD, "CREDIT_CARD"); + + public static final RedactionLevelEntityName ACCOUNT_NUMBER = + new RedactionLevelEntityName(Value.ACCOUNT_NUMBER, "ACCOUNT_NUMBER"); + + public static final RedactionLevelEntityName CREDIT_CARD_EXPIRATION = + new RedactionLevelEntityName(Value.CREDIT_CARD_EXPIRATION, "CREDIT_CARD_EXPIRATION"); + + public static final RedactionLevelEntityName SSN = new RedactionLevelEntityName(Value.SSN, "SSN"); + + public static final RedactionLevelEntityName EMAIL_ADDRESS = + new RedactionLevelEntityName(Value.EMAIL_ADDRESS, "EMAIL_ADDRESS"); + + public static final RedactionLevelEntityName STATISTICS = + new RedactionLevelEntityName(Value.STATISTICS, "STATISTICS"); + + private final Value value; + + private final String string; + + RedactionLevelEntityName(Value value, String string) { + this.value = value; + this.string = string; + } + + public Value getEnumValue() { + return value; + } + + @java.lang.Override + @JsonValue + public String toString() { + return this.string; + } + + @java.lang.Override + public boolean equals(Object other) { + return (this == other) + || (other instanceof RedactionLevelEntityName + && this.string.equals(((RedactionLevelEntityName) other).string)); + } + + @java.lang.Override + public int hashCode() { + return this.string.hashCode(); + } + + public T visit(Visitor visitor) { + switch (value) { + case PHONE_NUMBER: + return visitor.visitPhoneNumber(); + case USERNAME: + return visitor.visitUsername(); + case VEHICLE_ID: + return visitor.visitVehicleId(); + case RELIGION: + return visitor.visitReligion(); + case ORIGIN: + return visitor.visitOrigin(); + case PASSPORT_NUMBER: + return visitor.visitPassportNumber(); + case YEAR: + return visitor.visitYear(); + case CONDITION: + return visitor.visitCondition(); + case LOCATION: + return visitor.visitLocation(); + case LOCATION_CITY: + return visitor.visitLocationCity(); + case LOCATION_COUNTRY: + return visitor.visitLocationCountry(); + case PROJECT: + return visitor.visitProject(); + case LOCATION_ZIP: + return visitor.visitLocationZip(); + case NAME_MEDICAL_PROFESSIONAL: + return visitor.visitNameMedicalProfessional(); + case BANK_ACCOUNT: + return visitor.visitBankAccount(); + case OCCUPATION: + return visitor.visitOccupation(); + case TIME: + return visitor.visitTime(); + case LOCATION_STATE: + return visitor.visitLocationState(); + case EVENT: + return visitor.visitEvent(); + case POLITICAL_AFFILIATION: + return visitor.visitPoliticalAffiliation(); + case LANGUAGE: + return visitor.visitLanguage(); + case ZODIAC_SIGN: + return visitor.visitZodiacSign(); + case PRODUCT: + return visitor.visitProduct(); + case SEXUALITY: + return visitor.visitSexuality(); + case DURATION: + return visitor.visitDuration(); + case PHYSICAL_ATTRIBUTE: + return visitor.visitPhysicalAttribute(); + case LOCATION_ADDRESS_STREET: + return visitor.visitLocationAddressStreet(); + case MEDICAL_PROCESS: + return visitor.visitMedicalProcess(); + case LOCATION_ADDRESS: + return visitor.visitLocationAddress(); + case FILENAME: + return visitor.visitFilename(); + case DOB: + return visitor.visitDob(); + case ORGANIZATION_MEDICAL_FACILITY: + return visitor.visitOrganizationMedicalFacility(); + case ROUTING_NUMBER: + return visitor.visitRoutingNumber(); + case HEALTHCARE_NUMBER: + return visitor.visitHealthcareNumber(); + case GENDER: + return visitor.visitGender(); + case NAME: + return visitor.visitName(); + case DRUG: + return visitor.visitDrug(); + case NONE: + return visitor.visitNone(); + case DATE_INTERVAL: + return visitor.visitDateInterval(); + case MARITAL_STATUS: + return visitor.visitMaritalStatus(); + case CORPORATE_ACTION: + return visitor.visitCorporateAction(); + case AGE: + return visitor.visitAge(); + case CVV: + return visitor.visitCvv(); + case NAME_GIVEN: + return visitor.visitNameGiven(); + case DATE: + return visitor.visitDate(); + case DAY: + return visitor.visitDay(); + case EFFECT: + return visitor.visitEffect(); + case MEDICAL_CODE: + return visitor.visitMedicalCode(); + case NUMERICAL_PII: + return visitor.visitNumericalPii(); + case TREND: + return visitor.visitTrend(); + case INJURY: + return visitor.visitInjury(); + case DRIVER_LICENSE: + return visitor.visitDriverLicense(); + case ALL: + return visitor.visitAll(); + case ORGANIZATION_ID: + return visitor.visitOrganizationId(); + case PASSWORD: + return visitor.visitPassword(); + case DOSE: + return visitor.visitDose(); + case NAME_FAMILY: + return visitor.visitNameFamily(); + case MONEY: + return visitor.visitMoney(); + case URL: + return visitor.visitUrl(); + case LOCATION_COORDINATE: + return visitor.visitLocationCoordinate(); + case FINANCIAL_METRIC: + return visitor.visitFinancialMetric(); + case ORGANIZATION: + return visitor.visitOrganization(); + case IP_ADDRESS: + return visitor.visitIpAddress(); + case BLOOD_TYPE: + return visitor.visitBloodType(); + case MONTH: + return visitor.visitMonth(); + case CREDIT_CARD: + return visitor.visitCreditCard(); + case ACCOUNT_NUMBER: + return visitor.visitAccountNumber(); + case CREDIT_CARD_EXPIRATION: + return visitor.visitCreditCardExpiration(); + case SSN: + return visitor.visitSsn(); + case EMAIL_ADDRESS: + return visitor.visitEmailAddress(); + case STATISTICS: + return visitor.visitStatistics(); + case UNKNOWN: + default: + return visitor.visitUnknown(string); + } + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static RedactionLevelEntityName valueOf(String value) { + switch (value) { + case "PHONE_NUMBER": + return PHONE_NUMBER; + case "USERNAME": + return USERNAME; + case "VEHICLE_ID": + return VEHICLE_ID; + case "RELIGION": + return RELIGION; + case "ORIGIN": + return ORIGIN; + case "PASSPORT_NUMBER": + return PASSPORT_NUMBER; + case "YEAR": + return YEAR; + case "CONDITION": + return CONDITION; + case "LOCATION": + return LOCATION; + case "LOCATION_CITY": + return LOCATION_CITY; + case "LOCATION_COUNTRY": + return LOCATION_COUNTRY; + case "PROJECT": + return PROJECT; + case "LOCATION_ZIP": + return LOCATION_ZIP; + case "NAME_MEDICAL_PROFESSIONAL": + return NAME_MEDICAL_PROFESSIONAL; + case "BANK_ACCOUNT": + return BANK_ACCOUNT; + case "OCCUPATION": + return OCCUPATION; + case "TIME": + return TIME; + case "LOCATION_STATE": + return LOCATION_STATE; + case "EVENT": + return EVENT; + case "POLITICAL_AFFILIATION": + return POLITICAL_AFFILIATION; + case "LANGUAGE": + return LANGUAGE; + case "ZODIAC_SIGN": + return ZODIAC_SIGN; + case "PRODUCT": + return PRODUCT; + case "SEXUALITY": + return SEXUALITY; + case "DURATION": + return DURATION; + case "PHYSICAL_ATTRIBUTE": + return PHYSICAL_ATTRIBUTE; + case "LOCATION_ADDRESS_STREET": + return LOCATION_ADDRESS_STREET; + case "MEDICAL_PROCESS": + return MEDICAL_PROCESS; + case "LOCATION_ADDRESS": + return LOCATION_ADDRESS; + case "FILENAME": + return FILENAME; + case "DOB": + return DOB; + case "ORGANIZATION_MEDICAL_FACILITY": + return ORGANIZATION_MEDICAL_FACILITY; + case "ROUTING_NUMBER": + return ROUTING_NUMBER; + case "HEALTHCARE_NUMBER": + return HEALTHCARE_NUMBER; + case "GENDER": + return GENDER; + case "NAME": + return NAME; + case "DRUG": + return DRUG; + case "NONE": + return NONE; + case "DATE_INTERVAL": + return DATE_INTERVAL; + case "MARITAL_STATUS": + return MARITAL_STATUS; + case "CORPORATE_ACTION": + return CORPORATE_ACTION; + case "AGE": + return AGE; + case "CVV": + return CVV; + case "NAME_GIVEN": + return NAME_GIVEN; + case "DATE": + return DATE; + case "DAY": + return DAY; + case "EFFECT": + return EFFECT; + case "MEDICAL_CODE": + return MEDICAL_CODE; + case "NUMERICAL_PII": + return NUMERICAL_PII; + case "TREND": + return TREND; + case "INJURY": + return INJURY; + case "DRIVER_LICENSE": + return DRIVER_LICENSE; + case "ALL": + return ALL; + case "ORGANIZATION_ID": + return ORGANIZATION_ID; + case "PASSWORD": + return PASSWORD; + case "DOSE": + return DOSE; + case "NAME_FAMILY": + return NAME_FAMILY; + case "MONEY": + return MONEY; + case "URL": + return URL; + case "LOCATION_COORDINATE": + return LOCATION_COORDINATE; + case "FINANCIAL_METRIC": + return FINANCIAL_METRIC; + case "ORGANIZATION": + return ORGANIZATION; + case "IP_ADDRESS": + return IP_ADDRESS; + case "BLOOD_TYPE": + return BLOOD_TYPE; + case "MONTH": + return MONTH; + case "CREDIT_CARD": + return CREDIT_CARD; + case "ACCOUNT_NUMBER": + return ACCOUNT_NUMBER; + case "CREDIT_CARD_EXPIRATION": + return CREDIT_CARD_EXPIRATION; + case "SSN": + return SSN; + case "EMAIL_ADDRESS": + return EMAIL_ADDRESS; + case "STATISTICS": + return STATISTICS; + default: + return new RedactionLevelEntityName(Value.UNKNOWN, value); + } + } + + public enum Value { + NONE, + + BANK_ACCOUNT, + + CREDIT_CARD, + + CREDIT_CARD_EXPIRATION, + + CVV, + + DATE, + + DATE_INTERVAL, + + DOB, + + DRIVER_LICENSE, + + EMAIL_ADDRESS, + + HEALTHCARE_NUMBER, + + IP_ADDRESS, + + LOCATION, + + NAME, + + NUMERICAL_PII, + + PHONE_NUMBER, + + SSN, + + URL, + + VEHICLE_ID, + + MEDICAL_CODE, + + NAME_FAMILY, + + NAME_GIVEN, + + ACCOUNT_NUMBER, + + EVENT, + + FILENAME, + + GENDER, + + LANGUAGE, + + LOCATION_ADDRESS, + + LOCATION_CITY, + + LOCATION_COORDINATE, + + LOCATION_COUNTRY, + + LOCATION_STATE, + + LOCATION_ZIP, + + MARITAL_STATUS, + + MONEY, + + NAME_MEDICAL_PROFESSIONAL, + + OCCUPATION, + + ORGANIZATION, + + ORGANIZATION_MEDICAL_FACILITY, + + ORIGIN, + + PASSPORT_NUMBER, + + PASSWORD, + + PHYSICAL_ATTRIBUTE, + + POLITICAL_AFFILIATION, + + RELIGION, + + TIME, + + USERNAME, + + ZODIAC_SIGN, + + BLOOD_TYPE, + + CONDITION, + + DOSE, + + DRUG, + + INJURY, + + MEDICAL_PROCESS, + + STATISTICS, + + ROUTING_NUMBER, + + CORPORATE_ACTION, + + FINANCIAL_METRIC, + + PRODUCT, + + TREND, + + DURATION, + + LOCATION_ADDRESS_STREET, + + AGE, + + SEXUALITY, + + EFFECT, + + PROJECT, + + ORGANIZATION_ID, + + DAY, + + MONTH, + + YEAR, + + ALL, + + UNKNOWN + } + + public interface Visitor { + T visitNone(); + + T visitBankAccount(); + + T visitCreditCard(); + + T visitCreditCardExpiration(); + + T visitCvv(); + + T visitDate(); + + T visitDateInterval(); + + T visitDob(); + + T visitDriverLicense(); + + T visitEmailAddress(); + + T visitHealthcareNumber(); + + T visitIpAddress(); + + T visitLocation(); + + T visitName(); + + T visitNumericalPii(); + + T visitPhoneNumber(); + + T visitSsn(); + + T visitUrl(); + + T visitVehicleId(); + + T visitMedicalCode(); + + T visitNameFamily(); + + T visitNameGiven(); + + T visitAccountNumber(); + + T visitEvent(); + + T visitFilename(); + + T visitGender(); + + T visitLanguage(); + + T visitLocationAddress(); + + T visitLocationCity(); + + T visitLocationCoordinate(); + + T visitLocationCountry(); + + T visitLocationState(); + + T visitLocationZip(); + + T visitMaritalStatus(); + + T visitMoney(); + + T visitNameMedicalProfessional(); + + T visitOccupation(); + + T visitOrganization(); + + T visitOrganizationMedicalFacility(); + + T visitOrigin(); + + T visitPassportNumber(); + + T visitPassword(); + + T visitPhysicalAttribute(); + + T visitPoliticalAffiliation(); + + T visitReligion(); + + T visitTime(); + + T visitUsername(); + + T visitZodiacSign(); + + T visitBloodType(); + + T visitCondition(); + + T visitDose(); + + T visitDrug(); + + T visitInjury(); + + T visitMedicalProcess(); + + T visitStatistics(); + + T visitRoutingNumber(); + + T visitCorporateAction(); + + T visitFinancialMetric(); + + T visitProduct(); + + T visitTrend(); + + T visitDuration(); + + T visitLocationAddressStreet(); + + T visitAge(); + + T visitSexuality(); + + T visitEffect(); + + T visitProject(); + + T visitOrganizationId(); + + T visitDay(); + + T visitMonth(); + + T visitYear(); + + T visitAll(); + + T visitUnknown(String unknownType); + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/RedactionLevelRedactionType.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/RedactionLevelRedactionType.java new file mode 100644 index 00000000..5e9b0807 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/RedactionLevelRedactionType.java @@ -0,0 +1,106 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +public final class RedactionLevelRedactionType { + public static final RedactionLevelRedactionType DEFAULT = new RedactionLevelRedactionType(Value.DEFAULT, "DEFAULT"); + + public static final RedactionLevelRedactionType PLAIN_TEXT = + new RedactionLevelRedactionType(Value.PLAIN_TEXT, "PLAIN_TEXT"); + + public static final RedactionLevelRedactionType MASKED = new RedactionLevelRedactionType(Value.MASKED, "MASKED"); + + public static final RedactionLevelRedactionType REDACTED = + new RedactionLevelRedactionType(Value.REDACTED, "REDACTED"); + + private final Value value; + + private final String string; + + RedactionLevelRedactionType(Value value, String string) { + this.value = value; + this.string = string; + } + + public Value getEnumValue() { + return value; + } + + @java.lang.Override + @JsonValue + public String toString() { + return this.string; + } + + @java.lang.Override + public boolean equals(Object other) { + return (this == other) + || (other instanceof RedactionLevelRedactionType + && this.string.equals(((RedactionLevelRedactionType) other).string)); + } + + @java.lang.Override + public int hashCode() { + return this.string.hashCode(); + } + + public T visit(Visitor visitor) { + switch (value) { + case DEFAULT: + return visitor.visitDefault(); + case PLAIN_TEXT: + return visitor.visitPlainText(); + case MASKED: + return visitor.visitMasked(); + case REDACTED: + return visitor.visitRedacted(); + case UNKNOWN: + default: + return visitor.visitUnknown(string); + } + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static RedactionLevelRedactionType valueOf(String value) { + switch (value) { + case "DEFAULT": + return DEFAULT; + case "PLAIN_TEXT": + return PLAIN_TEXT; + case "MASKED": + return MASKED; + case "REDACTED": + return REDACTED; + default: + return new RedactionLevelRedactionType(Value.UNKNOWN, value); + } + } + + public enum Value { + DEFAULT, + + REDACTED, + + MASKED, + + PLAIN_TEXT, + + UNKNOWN + } + + public interface Visitor { + T visitDefault(); + + T visitRedacted(); + + T visitMasked(); + + T visitPlainText(); + + T visitUnknown(String unknownType); + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/ReidentifyFileResponseV2.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/ReidentifyFileResponseV2.java new file mode 100644 index 00000000..b7b8e3e5 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/ReidentifyFileResponseV2.java @@ -0,0 +1,198 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = ReidentifyFileResponseV2.Builder.class) +public final class ReidentifyFileResponseV2 { + private final Optional status; + + private final Optional outputType; + + private final Optional> output; + + private final Optional metrics; + + private final Map additionalProperties; + + private ReidentifyFileResponseV2( + Optional status, + Optional outputType, + Optional> output, + Optional metrics, + Map additionalProperties) { + this.status = status; + this.outputType = outputType; + this.output = output; + this.metrics = metrics; + this.additionalProperties = additionalProperties; + } + + /** + * @return Status of the reidentification request. + */ + @JsonProperty("status") + public Optional getStatus() { + return status; + } + + /** + * @return Data source of the output file. BASE64: Base64-encoded file string. SKYFLOW_ID: Reference file by vault ID. PRESIGNED_URL: S3 presigned URL of input file. + */ + @JsonProperty("outputType") + public Optional getOutputType() { + return outputType; + } + + /** + * @return Details of output files + */ + @JsonProperty("output") + public Optional> getOutput() { + return output; + } + + @JsonProperty("metrics") + public Optional getMetrics() { + return metrics; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof ReidentifyFileResponseV2 && equalTo((ReidentifyFileResponseV2) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(ReidentifyFileResponseV2 other) { + return status.equals(other.status) + && outputType.equals(other.outputType) + && output.equals(other.output) + && metrics.equals(other.metrics); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.status, this.outputType, this.output, this.metrics); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + private Optional status = Optional.empty(); + + private Optional outputType = Optional.empty(); + + private Optional> output = Optional.empty(); + + private Optional metrics = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(ReidentifyFileResponseV2 other) { + status(other.getStatus()); + outputType(other.getOutputType()); + output(other.getOutput()); + metrics(other.getMetrics()); + return this; + } + + /** + *

Status of the reidentification request.

+ */ + @JsonSetter(value = "status", nulls = Nulls.SKIP) + public Builder status(Optional status) { + this.status = status; + return this; + } + + public Builder status(ReidentifyFileResponseV2Status status) { + this.status = Optional.ofNullable(status); + return this; + } + + /** + *

Data source of the output file. BASE64: Base64-encoded file string. SKYFLOW_ID: Reference file by vault ID. PRESIGNED_URL: S3 presigned URL of input file.

+ */ + @JsonSetter(value = "outputType", nulls = Nulls.SKIP) + public Builder outputType(Optional outputType) { + this.outputType = outputType; + return this; + } + + public Builder outputType(ReidentifyFileResponseV2OutputType outputType) { + this.outputType = Optional.ofNullable(outputType); + return this; + } + + /** + *

Details of output files

+ */ + @JsonSetter(value = "output", nulls = Nulls.SKIP) + public Builder output(Optional> output) { + this.output = output; + return this; + } + + public Builder output(List output) { + this.output = Optional.ofNullable(output); + return this; + } + + @JsonSetter(value = "metrics", nulls = Nulls.SKIP) + public Builder metrics(Optional metrics) { + this.metrics = metrics; + return this; + } + + public Builder metrics(Metrics metrics) { + this.metrics = Optional.ofNullable(metrics); + return this; + } + + public ReidentifyFileResponseV2 build() { + return new ReidentifyFileResponseV2(status, outputType, output, metrics, additionalProperties); + } + + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/ReidentifyFileResponseV2OutputType.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/ReidentifyFileResponseV2OutputType.java new file mode 100644 index 00000000..ad4ea20f --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/ReidentifyFileResponseV2OutputType.java @@ -0,0 +1,97 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +public final class ReidentifyFileResponseV2OutputType { + public static final ReidentifyFileResponseV2OutputType BASE64 = + new ReidentifyFileResponseV2OutputType(Value.BASE64, "BASE64"); + + public static final ReidentifyFileResponseV2OutputType SKYFLOW_ID = + new ReidentifyFileResponseV2OutputType(Value.SKYFLOW_ID, "SKYFLOW_ID"); + + public static final ReidentifyFileResponseV2OutputType PRESIGNED_URL = + new ReidentifyFileResponseV2OutputType(Value.PRESIGNED_URL, "PRESIGNED_URL"); + + private final Value value; + + private final String string; + + ReidentifyFileResponseV2OutputType(Value value, String string) { + this.value = value; + this.string = string; + } + + public Value getEnumValue() { + return value; + } + + @java.lang.Override + @JsonValue + public String toString() { + return this.string; + } + + @java.lang.Override + public boolean equals(Object other) { + return (this == other) + || (other instanceof ReidentifyFileResponseV2OutputType + && this.string.equals(((ReidentifyFileResponseV2OutputType) other).string)); + } + + @java.lang.Override + public int hashCode() { + return this.string.hashCode(); + } + + public T visit(Visitor visitor) { + switch (value) { + case BASE64: + return visitor.visitBase64(); + case SKYFLOW_ID: + return visitor.visitSkyflowId(); + case PRESIGNED_URL: + return visitor.visitPresignedUrl(); + case UNKNOWN: + default: + return visitor.visitUnknown(string); + } + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static ReidentifyFileResponseV2OutputType valueOf(String value) { + switch (value) { + case "BASE64": + return BASE64; + case "SKYFLOW_ID": + return SKYFLOW_ID; + case "PRESIGNED_URL": + return PRESIGNED_URL; + default: + return new ReidentifyFileResponseV2OutputType(Value.UNKNOWN, value); + } + } + + public enum Value { + BASE64, + + SKYFLOW_ID, + + PRESIGNED_URL, + + UNKNOWN + } + + public interface Visitor { + T visitBase64(); + + T visitSkyflowId(); + + T visitPresignedUrl(); + + T visitUnknown(String unknownType); + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/ReidentifyFileResponseV2Status.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/ReidentifyFileResponseV2Status.java new file mode 100644 index 00000000..250bd1a9 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/ReidentifyFileResponseV2Status.java @@ -0,0 +1,119 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +public final class ReidentifyFileResponseV2Status { + public static final ReidentifyFileResponseV2Status IN_PROGRESS = + new ReidentifyFileResponseV2Status(Value.IN_PROGRESS, "IN_PROGRESS"); + + public static final ReidentifyFileResponseV2Status QUEUED = + new ReidentifyFileResponseV2Status(Value.QUEUED, "QUEUED"); + + public static final ReidentifyFileResponseV2Status SUCCESS = + new ReidentifyFileResponseV2Status(Value.SUCCESS, "SUCCESS"); + + public static final ReidentifyFileResponseV2Status UNKNOWN = + new ReidentifyFileResponseV2Status(Value.UNKNOWN, "UNKNOWN"); + + public static final ReidentifyFileResponseV2Status FAILED = + new ReidentifyFileResponseV2Status(Value.FAILED, "FAILED"); + + private final Value value; + + private final String string; + + ReidentifyFileResponseV2Status(Value value, String string) { + this.value = value; + this.string = string; + } + + public Value getEnumValue() { + return value; + } + + @java.lang.Override + @JsonValue + public String toString() { + return this.string; + } + + @java.lang.Override + public boolean equals(Object other) { + return (this == other) + || (other instanceof ReidentifyFileResponseV2Status + && this.string.equals(((ReidentifyFileResponseV2Status) other).string)); + } + + @java.lang.Override + public int hashCode() { + return this.string.hashCode(); + } + + public T visit(Visitor visitor) { + switch (value) { + case IN_PROGRESS: + return visitor.visitInProgress(); + case QUEUED: + return visitor.visitQueued(); + case SUCCESS: + return visitor.visitSuccess(); + case UNKNOWN: + return visitor.visitUnknown(); + case FAILED: + return visitor.visitFailed(); + case _UNKNOWN: + default: + return visitor.visitUnknown(string); + } + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static ReidentifyFileResponseV2Status valueOf(String value) { + switch (value) { + case "IN_PROGRESS": + return IN_PROGRESS; + case "QUEUED": + return QUEUED; + case "SUCCESS": + return SUCCESS; + case "UNKNOWN": + return UNKNOWN; + case "FAILED": + return FAILED; + default: + return new ReidentifyFileResponseV2Status(Value._UNKNOWN, value); + } + } + + public enum Value { + UNKNOWN, + + FAILED, + + SUCCESS, + + IN_PROGRESS, + + QUEUED, + + _UNKNOWN + } + + public interface Visitor { + T visitUnknown(); + + T visitFailed(); + + T visitSuccess(); + + T visitInProgress(); + + T visitQueued(); + + T visitUnknown(String unknownType); + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/ReidentifyStringResponseV2.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/ReidentifyStringResponseV2.java new file mode 100644 index 00000000..2c836067 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/ReidentifyStringResponseV2.java @@ -0,0 +1,134 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = ReidentifyStringResponseV2.Builder.class) +public final class ReidentifyStringResponseV2 { + private final Optional processedText; + + private final Optional metrics; + + private final Map additionalProperties; + + private ReidentifyStringResponseV2( + Optional processedText, Optional metrics, Map additionalProperties) { + this.processedText = processedText; + this.metrics = metrics; + this.additionalProperties = additionalProperties; + } + + /** + * @return Reidentified text. + */ + @JsonProperty("processedText") + public Optional getProcessedText() { + return processedText; + } + + @JsonProperty("metrics") + public Optional getMetrics() { + return metrics; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof ReidentifyStringResponseV2 && equalTo((ReidentifyStringResponseV2) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(ReidentifyStringResponseV2 other) { + return processedText.equals(other.processedText) && metrics.equals(other.metrics); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.processedText, this.metrics); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + private Optional processedText = Optional.empty(); + + private Optional metrics = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(ReidentifyStringResponseV2 other) { + processedText(other.getProcessedText()); + metrics(other.getMetrics()); + return this; + } + + /** + *

Reidentified text.

+ */ + @JsonSetter(value = "processedText", nulls = Nulls.SKIP) + public Builder processedText(Optional processedText) { + this.processedText = processedText; + return this; + } + + public Builder processedText(String processedText) { + this.processedText = Optional.ofNullable(processedText); + return this; + } + + @JsonSetter(value = "metrics", nulls = Nulls.SKIP) + public Builder metrics(Optional metrics) { + this.metrics = metrics; + return this; + } + + public Builder metrics(Metrics metrics) { + this.metrics = Optional.ofNullable(metrics); + return this; + } + + public ReidentifyStringResponseV2 build() { + return new ReidentifyStringResponseV2(processedText, metrics, additionalProperties); + } + + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/common/src/main/java/com/skyflow/generated/detect/rest/types/TransformationShiftDates.java b/common/src/main/java/com/skyflow/generated/detect/rest/types/TransformationShiftDates.java new file mode 100644 index 00000000..78879313 --- /dev/null +++ b/common/src/main/java/com/skyflow/generated/detect/rest/types/TransformationShiftDates.java @@ -0,0 +1,140 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.skyflow.generated.detect.rest.types; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = TransformationShiftDates.Builder.class) +public final class TransformationShiftDates { + private final Optional minDays; + + private final Optional maxDays; + + private final Map additionalProperties; + + private TransformationShiftDates( + Optional minDays, Optional maxDays, Map additionalProperties) { + this.minDays = minDays; + this.maxDays = maxDays; + this.additionalProperties = additionalProperties; + } + + /** + * @return Minimum number of days to shift dates. + */ + @JsonProperty("minDays") + public Optional getMinDays() { + return minDays; + } + + /** + * @return Maximum number of days to shift dates. + */ + @JsonProperty("maxDays") + public Optional getMaxDays() { + return maxDays; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof TransformationShiftDates && equalTo((TransformationShiftDates) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(TransformationShiftDates other) { + return minDays.equals(other.minDays) && maxDays.equals(other.maxDays); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.minDays, this.maxDays); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + private Optional minDays = Optional.empty(); + + private Optional maxDays = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(TransformationShiftDates other) { + minDays(other.getMinDays()); + maxDays(other.getMaxDays()); + return this; + } + + /** + *

Minimum number of days to shift dates.

+ */ + @JsonSetter(value = "minDays", nulls = Nulls.SKIP) + public Builder minDays(Optional minDays) { + this.minDays = minDays; + return this; + } + + public Builder minDays(Integer minDays) { + this.minDays = Optional.ofNullable(minDays); + return this; + } + + /** + *

Maximum number of days to shift dates.

+ */ + @JsonSetter(value = "maxDays", nulls = Nulls.SKIP) + public Builder maxDays(Optional maxDays) { + this.maxDays = maxDays; + return this; + } + + public Builder maxDays(Integer maxDays) { + this.maxDays = Optional.ofNullable(maxDays); + return this; + } + + public TransformationShiftDates build() { + return new TransformationShiftDates(minDays, maxDays, additionalProperties); + } + + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/common/src/main/java/com/skyflow/logs/ErrorLogs.java b/common/src/main/java/com/skyflow/logs/ErrorLogs.java index 418d08bc..2051cedc 100644 --- a/common/src/main/java/com/skyflow/logs/ErrorLogs.java +++ b/common/src/main/java/com/skyflow/logs/ErrorLogs.java @@ -1,6 +1,34 @@ package com.skyflow.logs; public enum ErrorLogs { + CHECK_GUARDRAILS_REQUEST_NULL("Invalid %s1 request. CheckGuardrails request can not be null."), + INVALID_TEXT_IN_CHECK_GUARDRAILS("Invalid %s1 request. The text field is required and must be a non-empty string. Specify a valid text."), + CHECK_GUARDRAILS_REQUEST_REJECTED("Check guardrails request resulted in failure."), + GET_RUN_REQUEST_NULL("Invalid %s1 request. GetRun request can not be null."), + INVALID_RUN_ID_IN_GET_RUN("Invalid %s1 request. The runId field is required and must be a non-empty string. Specify a valid run id."), + GET_RUN_REQUEST_REJECTED("Get run request resulted in failure."), + DEIDENTIFY_STRING_REQUEST_NULL("Invalid %s1 request. DeidentifyString request can not be null."), + INVALID_TEXT_IN_DEIDENTIFY_STRING("Invalid %s1 request. The text field is required and must be a non-empty string. Specify a valid text."), + DEIDENTIFY_STRING_REQUEST_REJECTED("DeIdentify string request resulted in failure."), + REIDENTIFY_STRING_REQUEST_NULL("Invalid %s1 request. ReidentifyString request can not be null."), + INVALID_TEXT_IN_REIDENTIFY_STRING("Invalid %s1 request. The text field is required and must be a non-empty string. Specify a valid text."), + REIDENTIFY_STRING_REQUEST_REJECTED("ReIdentify string request resulted in failure."), + DEIDENTIFY_FILE_REQUEST_NULL("Invalid %s1 request. DeidentifyFile request can not be null."), + INVALID_DATA_SOURCE_IN_DEIDENTIFY_FILE("Invalid %s1 request. The dataSource field is required unless a file is supplied."), + INVALID_VALUE_IN_DEIDENTIFY_FILE("Invalid %s1 request. The value field is required and must be a non-empty string unless a file is supplied."), + MULTIPLE_FILE_SOURCES_IN_DEIDENTIFY_FILE("Invalid %s1 request. Specify either value or file, not both."), + FILE_NOT_FOUND_IN_DEIDENTIFY_FILE("Invalid %s1 request. The file to deidentify was not found."), + FAILED_TO_ENCODE_FILE_IN_DEIDENTIFY_FILE("Invalid %s1 request. Failed to read and encode the file to deidentify."), + INVALID_WAIT_TIME_IN_POLL_OPTIONS("Invalid %s1 request. waitTime in pollOptions must be between 1 and 300."), + INVALID_MAX_ATTEMPTS_IN_POLL_OPTIONS("Invalid %s1 request. maxAttempts in pollOptions must be between 1 and 23."), + OUTPUT_DIRECTORY_WITHOUT_POLL_OPTIONS("Invalid %s1 request. outputDirectory requires pollOptions."), + DEIDENTIFY_FILE_REQUEST_REJECTED("DeIdentify file request resulted in failure."), + OUTPUT_DIRECTORY_NOT_CREATED("Could not create output directory %s1."), + OUTPUT_FILE_NOT_WRITTEN("Could not write processed output to %s1."), + REIDENTIFY_FILE_REQUEST_NULL("Invalid %s1 request. ReidentifyFile request can not be null."), + INVALID_DATA_SOURCE_IN_REIDENTIFY_FILE("Invalid %s1 request. The dataSource field is required. Specify BASE64, SKYFLOW_ID or PRESIGNED_URL."), + INVALID_VALUE_IN_REIDENTIFY_FILE("Invalid %s1 request. The value field is required and must be a non-empty string. Specify the base64 content, skyflow id or presigned URL of the file."), + REIDENTIFY_FILE_REQUEST_REJECTED("ReIdentify file request resulted in failure."), // Client initialization VAULT_CONFIG_EXISTS("Vault config with vault ID %s1 already exists."), VAULT_CONFIG_DOES_NOT_EXIST("Vault config with vault ID %s1 doesn't exist."), @@ -184,7 +212,6 @@ public enum ErrorLogs { REIDENTIFY_TEXT_REQUEST_NULL("Invalid %s1 request. ReidentifyText request can not be null."), INVALID_TEXT_IN_REIDENTIFY("Invalid %s1 request. The text field is required and must be a non-empty string. Specify a valid text."), REIDENTIFY_TEXT_REQUEST_REJECTED("ReIdentify text request resulted in failure."), - DEIDENTIFY_FILE_REQUEST_REJECTED("DeIdentify file request resulted in failure."), GET_DETECT_RUN_REQUEST_REJECTED("Get detect run request resulted in failure."), INVALID_NULL_FILE_IN_DEIDENTIFY_FILE("Invalid %s1 request. The file field is required and must not be null. Specify a valid file."), FILE_NOT_FOUND_TO_DEIDENTIFY("Invalid %s1 request. The file field is required and must not be empty. Specify a valid file."), diff --git a/common/src/main/java/com/skyflow/logs/InfoLogs.java b/common/src/main/java/com/skyflow/logs/InfoLogs.java index b4045229..3c332232 100644 --- a/common/src/main/java/com/skyflow/logs/InfoLogs.java +++ b/common/src/main/java/com/skyflow/logs/InfoLogs.java @@ -1,6 +1,37 @@ package com.skyflow.logs; public enum InfoLogs { + CHECK_GUARDRAILS_TRIGGERED("Check guardrails method triggered."), + VALIDATE_CHECK_GUARDRAILS_REQUEST("Validating check guardrails request."), + CHECK_GUARDRAILS_REQUEST_RESOLVED("Check guardrails request resolved."), + CHECK_GUARDRAILS_SUCCESS("Guardrails check completed."), + GET_RUN_TRIGGERED("Get run method triggered."), + VALIDATE_GET_RUN_REQUEST("Validating get run request."), + GET_RUN_REQUEST_RESOLVED("Get run request resolved."), + GET_RUN_SUCCESS("Detect run retrieved."), + DEIDENTIFY_STRING_TRIGGERED("DeIdentify string method triggered."), + VALIDATE_DEIDENTIFY_STRING_REQUEST("Validating deidentify string request."), + DEIDENTIFY_STRING_REQUEST_RESOLVED("DeIdentify string request resolved."), + DEIDENTIFY_STRING_SUCCESS("String data de-identified."), + REIDENTIFY_STRING_TRIGGERED("ReIdentify string method triggered."), + VALIDATE_REIDENTIFY_STRING_REQUEST("Validating reidentify string request."), + REIDENTIFY_STRING_REQUEST_RESOLVED("ReIdentify string request resolved."), + REIDENTIFY_STRING_SUCCESS("String data re-identified."), + DEIDENTIFY_FILE_TRIGGERED("DeIdentify file method triggered."), + VALIDATE_DEIDENTIFY_FILE_REQUEST("Validating deidentify file request."), + DEIDENTIFY_FILE_REQUEST_RESOLVED("DeIdentify file request resolved."), + DEIDENTIFY_FILE_RUN_SUBMITTED("DeIdentify file run %s1 submitted; retrieve it with getRun."), + DEIDENTIFY_FILE_SUCCESS("File data de-identified."), + POLL_RUN_STARTED("Polling run %s1 for up to %s2 seconds and %s3 attempts."), + POLL_RUN_TERMINAL("Run %s1 reached status %s2 after %s3 attempts."), + POLL_RUN_BUDGET_EXHAUSTED("Run %s1 still %s2 after %s3 attempts and %s4 seconds; returning run id."), + POLL_RUN_INTERRUPTED("Polling of run %s1 interrupted while %s2; returning run id."), + OUTPUT_FILE_WRITTEN("Processed output written to %s1."), + OUTPUT_NOT_WRITTEN_FOR_OUTPUT_TYPE("Outputs delivered as %s1 are not written to disk."), + REIDENTIFY_FILE_TRIGGERED("ReIdentify file method triggered."), + VALIDATE_REIDENTIFY_FILE_REQUEST("Validating reidentify file request."), + REIDENTIFY_FILE_REQUEST_RESOLVED("ReIdentify file request resolved."), + REIDENTIFY_FILE_SUCCESS("File data re-identified."), // Client initialization CLIENT_INITIALIZED("Initialized skyflow client."), VALIDATING_VAULT_CONFIG("Validating vault config."), @@ -91,10 +122,6 @@ public enum InfoLogs { VALIDATE_REIDENTIFY_TEXT_REQUEST("Validating reidentify text request."), REIDENTIFY_TEXT_TRIGGERED("ReIdentify text method triggered."), REIDENTIFY_TEXT_REQUEST_RESOLVED("ReIdentify text request resolved."), - DEIDENTIFY_FILE_TRIGGERED("DeIdentify file method triggered."), - VALIDATE_DEIDENTIFY_FILE_REQUEST("Validating deidentify file request."), - DEIDENTIFY_FILE_REQUEST_RESOLVED("DeIdentify file request resolved."), - DEIDENTIFY_FILE_SUCCESS("File deidentified successfully."), GET_DETECT_RUN_TRIGGERED("Get detect run method triggered."), VALIDATE_GET_DETECT_RUN_REQUEST("Validating get detect run request."), REIDENTIFY_TEXT_SUCCESS("Text data re-identified."), diff --git a/common/src/main/java/com/skyflow/serviceaccount/util/BearerToken.java b/common/src/main/java/com/skyflow/serviceaccount/util/BearerToken.java index 882df51c..a98f4e3a 100644 --- a/common/src/main/java/com/skyflow/serviceaccount/util/BearerToken.java +++ b/common/src/main/java/com/skyflow/serviceaccount/util/BearerToken.java @@ -2,7 +2,7 @@ import com.google.gson.*; import com.skyflow.errors.ErrorCode; -import com.skyflow.errors.ErrorMessage; +import com.skyflow.errors.BaseErrorMessage; import com.skyflow.errors.SkyflowException; import com.skyflow.generated.auth.rest.ApiClient; import com.skyflow.generated.auth.rest.ApiClientBuilder; @@ -56,7 +56,7 @@ private static V1GetAuthTokenResponse generateBearerTokenFromCredentials( try { if (credentialsFile == null) { LogUtil.printErrorLog(ErrorLogs.INVALID_CREDENTIALS_FILE.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.InvalidCredentials.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.InvalidCredentials.getMessage()); } FileReader reader = new FileReader(String.valueOf(credentialsFile)); try { @@ -68,11 +68,11 @@ private static V1GetAuthTokenResponse generateBearerTokenFromCredentials( } catch (JsonSyntaxException | IllegalStateException e) { LogUtil.printErrorLog(ErrorLogs.INVALID_CREDENTIALS_FILE_FORMAT.getLog()); throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseUtils.parameterizedString( - ErrorMessage.FileInvalidJson.getMessage(), credentialsFile.getPath())); + BaseErrorMessage.FileInvalidJson.getMessage(), credentialsFile.getPath())); } catch (FileNotFoundException e) { LogUtil.printErrorLog(ErrorLogs.CREDENTIALS_FILE_NOT_FOUND.getLog()); throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseUtils.parameterizedString( - ErrorMessage.FileNotFound.getMessage(), credentialsFile.getPath())); + BaseErrorMessage.FileNotFound.getMessage(), credentialsFile.getPath())); } } @@ -83,14 +83,14 @@ private static V1GetAuthTokenResponse generateBearerTokenFromCredentialString( try { if (credentials == null || credentials.isEmpty()) { LogUtil.printErrorLog(ErrorLogs.INVALID_CREDENTIALS_STRING.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.InvalidCredentials.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.InvalidCredentials.getMessage()); } JsonObject serviceAccountCredentials = JsonParser.parseString(credentials).getAsJsonObject(); return getBearerTokenFromCredentials(serviceAccountCredentials, context, roles); } catch (JsonSyntaxException | IllegalStateException e) { LogUtil.printErrorLog(ErrorLogs.INVALID_CREDENTIALS_STRING_FORMAT.getLog()); throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), - ErrorMessage.CredentialsStringInvalidJson.getMessage()); + BaseErrorMessage.CredentialsStringInvalidJson.getMessage()); } } @@ -101,7 +101,7 @@ private static V1GetAuthTokenResponse getBearerTokenFromCredentials( JsonElement privateKey = credentials.get("privateKey"); if (privateKey == null) { LogUtil.printErrorLog(ErrorLogs.PRIVATE_KEY_IS_REQUIRED.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.MissingPrivateKey.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.MissingPrivateKey.getMessage()); } // Accept both new-form keys (clientId/keyId/tokenUri) and legacy all-caps form for migration @@ -114,7 +114,7 @@ private static V1GetAuthTokenResponse getBearerTokenFromCredentials( } if (clientId == null) { LogUtil.printErrorLog(ErrorLogs.CLIENT_ID_IS_REQUIRED.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.MissingClientId.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.MissingClientId.getMessage()); } JsonElement keyId = credentials.get("keyId"); @@ -126,7 +126,7 @@ private static V1GetAuthTokenResponse getBearerTokenFromCredentials( } if (keyId == null) { LogUtil.printErrorLog(ErrorLogs.KEY_ID_IS_REQUIRED.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.MissingKeyId.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.MissingKeyId.getMessage()); } JsonElement tokenUri = credentials.get("tokenUri"); @@ -138,7 +138,7 @@ private static V1GetAuthTokenResponse getBearerTokenFromCredentials( } if (tokenUri == null) { LogUtil.printErrorLog(ErrorLogs.TOKEN_URI_IS_REQUIRED.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.MissingTokenUri.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.MissingTokenUri.getMessage()); } PrivateKey pvtKey = BaseUtils.getPrivateKeyFromPem(privateKey.getAsString()); @@ -160,7 +160,7 @@ private static V1GetAuthTokenResponse getBearerTokenFromCredentials( return authenticationApi.authenticationServiceGetAuthToken(authTokenBuilder.build()); } catch (MalformedURLException e) { LogUtil.printErrorLog(ErrorLogs.INVALID_TOKEN_URI.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.InvalidTokenUri.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.InvalidTokenUri.getMessage()); } catch (ApiClientApiException e) { String bodyString = GSON.toJson(e.body()); LogUtil.printErrorLog(ErrorLogs.BEARER_TOKEN_REJECTED.getLog()); @@ -218,7 +218,7 @@ public synchronized String getBearerToken() throws SkyflowException { private static String extractAccessToken(V1GetAuthTokenResponse response) throws SkyflowException { if (!response.getAccessToken().isPresent()) { LogUtil.printErrorLog(ErrorLogs.MISSING_ACCESS_TOKEN.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.MissingAccessToken.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.MissingAccessToken.getMessage()); } return response.getAccessToken().get(); } diff --git a/common/src/main/java/com/skyflow/serviceaccount/util/SignedDataTokens.java b/common/src/main/java/com/skyflow/serviceaccount/util/SignedDataTokens.java index 630a0aee..a4a2c57d 100644 --- a/common/src/main/java/com/skyflow/serviceaccount/util/SignedDataTokens.java +++ b/common/src/main/java/com/skyflow/serviceaccount/util/SignedDataTokens.java @@ -5,7 +5,7 @@ import com.google.gson.JsonParser; import com.google.gson.JsonSyntaxException; import com.skyflow.errors.ErrorCode; -import com.skyflow.errors.ErrorMessage; +import com.skyflow.errors.BaseErrorMessage; import com.skyflow.errors.SkyflowException; import com.skyflow.logs.ErrorLogs; import com.skyflow.logs.InfoLogs; @@ -49,7 +49,7 @@ private static List generateSignedTokenFromCredentialsF try { if (credentialsFile == null) { LogUtil.printErrorLog(ErrorLogs.INVALID_CREDENTIALS_FILE.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.InvalidCredentials.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.InvalidCredentials.getMessage()); } FileReader reader = new FileReader(String.valueOf(credentialsFile)); try { @@ -61,11 +61,11 @@ private static List generateSignedTokenFromCredentialsF } catch (JsonSyntaxException e) { LogUtil.printErrorLog(ErrorLogs.INVALID_CREDENTIALS_FILE_FORMAT.getLog()); throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseUtils.parameterizedString( - ErrorMessage.FileInvalidJson.getMessage(), credentialsFile.getPath())); + BaseErrorMessage.FileInvalidJson.getMessage(), credentialsFile.getPath())); } catch (FileNotFoundException e) { LogUtil.printErrorLog(ErrorLogs.CREDENTIALS_FILE_NOT_FOUND.getLog()); throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseUtils.parameterizedString( - ErrorMessage.FileNotFound.getMessage(), credentialsFile.getPath())); + BaseErrorMessage.FileNotFound.getMessage(), credentialsFile.getPath())); } return responseToken; } @@ -78,14 +78,14 @@ private static List generateSignedTokensFromCredentials try { if (credentials == null || credentials.isEmpty()) { LogUtil.printErrorLog(ErrorLogs.INVALID_CREDENTIALS_STRING.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.InvalidCredentials.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.InvalidCredentials.getMessage()); } JsonObject serviceAccountCredentials = JsonParser.parseString(credentials).getAsJsonObject(); responseToken = generateSignedTokensFromCredentials(serviceAccountCredentials, dataTokens, timeToLive, context); } catch (JsonSyntaxException e) { LogUtil.printErrorLog(ErrorLogs.INVALID_CREDENTIALS_STRING_FORMAT.getLog()); throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), - ErrorMessage.CredentialsStringInvalidJson.getMessage()); + BaseErrorMessage.CredentialsStringInvalidJson.getMessage()); } return responseToken; } @@ -98,7 +98,7 @@ private static List generateSignedTokensFromCredentials JsonElement privateKey = credentials.get("privateKey"); if (privateKey == null) { LogUtil.printErrorLog(ErrorLogs.PRIVATE_KEY_IS_REQUIRED.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.MissingPrivateKey.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.MissingPrivateKey.getMessage()); } // Accept both new-form keys (clientId/keyId) and legacy all-caps form for migration @@ -111,7 +111,7 @@ private static List generateSignedTokensFromCredentials } if (clientId == null) { LogUtil.printErrorLog(ErrorLogs.CLIENT_ID_IS_REQUIRED.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.MissingClientId.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.MissingClientId.getMessage()); } JsonElement keyId = credentials.get("keyId"); @@ -123,7 +123,7 @@ private static List generateSignedTokensFromCredentials } if (keyId == null) { LogUtil.printErrorLog(ErrorLogs.KEY_ID_IS_REQUIRED.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.MissingKeyId.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.MissingKeyId.getMessage()); } PrivateKey pvtKey = BaseUtils.getPrivateKeyFromPem(privateKey.getAsString()); signedDataTokens = getSignedToken( diff --git a/common/src/main/java/com/skyflow/serviceaccount/util/Token.java b/common/src/main/java/com/skyflow/serviceaccount/util/Token.java index 0359d944..19edf0c8 100644 --- a/common/src/main/java/com/skyflow/serviceaccount/util/Token.java +++ b/common/src/main/java/com/skyflow/serviceaccount/util/Token.java @@ -5,7 +5,7 @@ import com.google.gson.JsonParser; import com.google.gson.JsonSyntaxException; import com.skyflow.errors.ErrorCode; -import com.skyflow.errors.ErrorMessage; +import com.skyflow.errors.BaseErrorMessage; import com.skyflow.errors.SkyflowException; import com.skyflow.logs.ErrorLogs; import com.skyflow.logs.InfoLogs; @@ -29,7 +29,7 @@ public static boolean isExpired(String token) { JsonElement exp = decoded(token).get("exp"); if (exp == null) { LogUtil.printErrorLog(ErrorLogs.INVALID_BEARER_TOKEN.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.JwtDecodeError.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.JwtDecodeError.getMessage()); } expiryTime = exp.getAsLong(); @@ -44,14 +44,14 @@ static JsonObject decoded(String encodedToken) throws JsonSyntaxException, Skyfl String[] split = encodedToken.split("\\."); if (split.length < 3) { LogUtil.printErrorLog(ErrorLogs.INVALID_BEARER_TOKEN.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.JwtDecodeError.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.JwtDecodeError.getMessage()); } try { byte[] decodedBytes = Base64.getUrlDecoder().decode(split[1]); return JsonParser.parseString(new String(decodedBytes, StandardCharsets.UTF_8)).getAsJsonObject(); } catch (IllegalArgumentException | IllegalStateException e) { LogUtil.printErrorLog(ErrorLogs.INVALID_BEARER_TOKEN.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.JwtDecodeError.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.JwtDecodeError.getMessage()); } } } diff --git a/common/src/main/java/com/skyflow/utils/BaseConstants.java b/common/src/main/java/com/skyflow/utils/BaseConstants.java index e9946a40..8d0ea8d8 100644 --- a/common/src/main/java/com/skyflow/utils/BaseConstants.java +++ b/common/src/main/java/com/skyflow/utils/BaseConstants.java @@ -27,6 +27,7 @@ public class BaseConstants { public static final String SDK_METRIC_RUNTIME_DETAILS_PREFIX = "Java@"; public static final String SDK_AUTH_HEADER_KEY = "x-skyflow-authorization"; public static final String SDK_METRICS_HEADER_KEY = "sky-metadata"; + public static final String PROCESSED_FILE_NAME_PREFIX = "processed-"; public static final String REQUEST_ID_HEADER_KEY = "x-request-id"; public static final String ERROR_FROM_CLIENT_HEADER_KEY = "error-from-client"; diff --git a/common/src/main/java/com/skyflow/utils/BaseUtils.java b/common/src/main/java/com/skyflow/utils/BaseUtils.java index e536c111..8e076c1b 100644 --- a/common/src/main/java/com/skyflow/utils/BaseUtils.java +++ b/common/src/main/java/com/skyflow/utils/BaseUtils.java @@ -15,7 +15,7 @@ import com.skyflow.config.BaseCredentials; import com.skyflow.enums.Env; import com.skyflow.errors.ErrorCode; -import com.skyflow.errors.ErrorMessage; +import com.skyflow.errors.BaseErrorMessage; import com.skyflow.errors.SkyflowException; import com.skyflow.logs.ErrorLogs; import com.skyflow.logs.InfoLogs; @@ -87,11 +87,11 @@ public static PrivateKey getPrivateKeyFromPem(String pemKey) throws SkyflowExcep privateKey = parsePkcs8PrivateKey(Base64.getDecoder().decode(privateKeyContent)); } catch (IllegalArgumentException e) { LogUtil.printErrorLog(ErrorLogs.INVALID_KEY_SPEC.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.InvalidKeySpec.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.InvalidKeySpec.getMessage()); } } else { LogUtil.printErrorLog(ErrorLogs.JWT_INVALID_FORMAT.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.JwtInvalidFormat.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.JwtInvalidFormat.getMessage()); } return privateKey; } @@ -165,10 +165,10 @@ private static PrivateKey parsePkcs8PrivateKey(byte[] pkcs8Bytes) throws Skyflow privateKey = keyFactory.generatePrivate(keySpec); } catch (NoSuchAlgorithmException e) { LogUtil.printErrorLog(ErrorLogs.INVALID_ALGORITHM.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.InvalidAlgorithm.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.InvalidAlgorithm.getMessage()); } catch (InvalidKeySpecException e) { LogUtil.printErrorLog(ErrorLogs.INVALID_KEY_SPEC.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.InvalidKeySpec.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.InvalidKeySpec.getMessage()); } return privateKey; } diff --git a/common/src/main/java/com/skyflow/utils/validations/BaseValidations.java b/common/src/main/java/com/skyflow/utils/validations/BaseValidations.java index b542589a..89bfb501 100644 --- a/common/src/main/java/com/skyflow/utils/validations/BaseValidations.java +++ b/common/src/main/java/com/skyflow/utils/validations/BaseValidations.java @@ -2,7 +2,7 @@ import com.skyflow.config.BaseCredentials; import com.skyflow.errors.ErrorCode; -import com.skyflow.errors.ErrorMessage; +import com.skyflow.errors.BaseErrorMessage; import com.skyflow.errors.SkyflowException; import com.skyflow.logs.ErrorLogs; import com.skyflow.utils.BaseConstants; @@ -21,7 +21,7 @@ public class BaseValidations { public static void validateCredentials(BaseCredentials credentials) throws SkyflowException { if (credentials == null) { LogUtil.printErrorLog(ErrorLogs.CREDENTIALS_IS_NULL.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.NullCredentials.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.NullCredentials.getMessage()); } int nonNullMembers = 0; String path = credentials.getPath(); @@ -39,38 +39,38 @@ public static void validateCredentials(BaseCredentials credentials) throws Skyfl if (nonNullMembers > 1) { LogUtil.printErrorLog(ErrorLogs.MULTIPLE_TOKEN_GENERATION_MEANS_PASSED.getLog()); throw new SkyflowException( - ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.MultipleTokenGenerationMeansPassed.getMessage() + ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.MultipleTokenGenerationMeansPassed.getMessage() ); } else if (nonNullMembers < 1) { LogUtil.printErrorLog(ErrorLogs.NO_TOKEN_GENERATION_MEANS_PASSED.getLog()); throw new SkyflowException( - ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.NoTokenGenerationMeansPassed.getMessage() + ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.NoTokenGenerationMeansPassed.getMessage() ); } else if (path != null && path.trim().isEmpty()) { LogUtil.printErrorLog(ErrorLogs.EMPTY_CREDENTIALS_PATH.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyCredentialFilePath.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.EmptyCredentialFilePath.getMessage()); } else if (credentialsString != null && credentialsString.trim().isEmpty()) { LogUtil.printErrorLog(ErrorLogs.EMPTY_CREDENTIALS_STRING.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyCredentialsString.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.EmptyCredentialsString.getMessage()); } else if (token != null && token.trim().isEmpty()) { LogUtil.printErrorLog(ErrorLogs.EMPTY_TOKEN_VALUE.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyToken.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.EmptyToken.getMessage()); } else if (apiKey != null) { if (apiKey.trim().isEmpty()) { LogUtil.printErrorLog(ErrorLogs.EMPTY_API_KEY_VALUE.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyApikey.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.EmptyApikey.getMessage()); } else { Pattern pattern = Pattern.compile(BaseConstants.API_KEY_REGEX); Matcher matcher = pattern.matcher(apiKey); if (!matcher.matches()) { LogUtil.printErrorLog(ErrorLogs.INVALID_API_KEY.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.InvalidApikey.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.InvalidApikey.getMessage()); } } } else if (roles != null) { if (roles.isEmpty()) { LogUtil.printErrorLog(ErrorLogs.EMPTY_ROLES.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyRoles.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.EmptyRoles.getMessage()); } else { for (int index = 0; index < roles.size(); index++) { String role = roles.get(index); @@ -78,7 +78,7 @@ public static void validateCredentials(BaseCredentials credentials) throws Skyfl LogUtil.printErrorLog(BaseUtils.parameterizedString( ErrorLogs.EMPTY_OR_NULL_ROLE_IN_ROLES.getLog(), Integer.toString(index) )); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyRoleInRoles.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.EmptyRoleInRoles.getMessage()); } } } @@ -88,13 +88,13 @@ public static void validateCredentials(BaseCredentials credentials) throws Skyfl String ctxStr = (String) context; if (ctxStr.trim().isEmpty()) { LogUtil.printErrorLog(ErrorLogs.EMPTY_OR_NULL_CONTEXT.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyContext.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.EmptyContext.getMessage()); } } else if (context instanceof Map) { Map ctxMap = (Map) context; if (ctxMap.isEmpty()) { LogUtil.printErrorLog(ErrorLogs.EMPTY_OR_NULL_CONTEXT.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyContext.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.EmptyContext.getMessage()); } Pattern ctxKeyPattern = Pattern.compile(BaseConstants.CONTEXT_KEY_REGEX); for (Object key : ctxMap.keySet()) { @@ -103,12 +103,12 @@ public static void validateCredentials(BaseCredentials credentials) throws Skyfl LogUtil.printErrorLog(BaseUtils.parameterizedString( ErrorLogs.INVALID_CONTEXT_MAP_KEY.getLog(), keyStr)); throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), - BaseUtils.parameterizedString(ErrorMessage.InvalidContextMapKey.getMessage(), keyStr)); + BaseUtils.parameterizedString(BaseErrorMessage.InvalidContextMapKey.getMessage(), keyStr)); } } } else { LogUtil.printErrorLog(ErrorLogs.INVALID_CONTEXT_TYPE.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.InvalidContextType.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.InvalidContextType.getMessage()); } } } diff --git a/common/src/main/java/com/skyflow/vault/controller/BaseDetectController.java b/common/src/main/java/com/skyflow/vault/controller/BaseDetectController.java new file mode 100644 index 00000000..71f6217d --- /dev/null +++ b/common/src/main/java/com/skyflow/vault/controller/BaseDetectController.java @@ -0,0 +1,379 @@ +package com.skyflow.vault.controller; + +import com.skyflow.BaseVaultClient; +import com.skyflow.config.BaseCredentials; +import com.skyflow.config.BaseVaultConfig; +import com.skyflow.detect.CheckGuardrailsRequest; +import com.skyflow.detect.CheckGuardrailsResponse; +import com.skyflow.detect.DeidentifyStringRequest; +import com.skyflow.detect.DeidentifyStringResponse; +import com.skyflow.detect.GetRunRequest; +import com.skyflow.detect.GetRunResponse; +import com.skyflow.detect.ReidentifyStringRequest; +import com.skyflow.detect.ReidentifyStringResponse; +import com.skyflow.detect.internal.DetectRequestMapper; +import com.skyflow.detect.internal.DetectResponseMapper; +import com.skyflow.detect.internal.DetectValidations; +import com.skyflow.errors.SkyflowException; +import com.skyflow.generated.detect.rest.core.ApiClientApiException; +import com.skyflow.generated.detect.rest.core.ApiClientException; +import com.skyflow.generated.detect.rest.core.ClientOptions; +import com.skyflow.generated.detect.rest.core.Environment; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import com.skyflow.generated.detect.rest.core.RequestOptions; +import com.skyflow.generated.detect.rest.resources.filesv2.FilesV2Client; +import com.skyflow.generated.detect.rest.resources.filesv2.requests.GetRunV2Request; +import com.skyflow.generated.detect.rest.resources.guardrailsv2.GuardrailsV2Client; +import com.skyflow.generated.detect.rest.resources.guardrailsv2.requests.DetectGuardrailsRequestV2; +import com.skyflow.generated.detect.rest.resources.stringsv2.StringsV2Client; +import com.skyflow.generated.detect.rest.resources.stringsv2.requests.DeidentifyStringRequestV2; +import com.skyflow.generated.detect.rest.resources.stringsv2.requests.ReidentifyStringRequestV2; +import com.skyflow.generated.detect.rest.types.DeidentifyStringResponseV2; +import com.skyflow.generated.detect.rest.types.DetectGuardrailsResponseV2; +import com.skyflow.generated.detect.rest.types.DetectRunsResponseV2; +import com.skyflow.generated.detect.rest.types.ReidentifyStringResponseV2; +import com.skyflow.logs.ErrorLogs; +import com.skyflow.logs.InfoLogs; +import com.skyflow.utils.BaseConstants; +import com.skyflow.utils.logger.LogUtil; + +/** + * Shared implementation of the Skyflow Detect V2 API. Each SDK's {@code DetectController} + * extends this class and supplies only what differs per vault type: how the vault URL is + * resolved, the SDK metrics header, and any vault-level timeout and retry overrides. + * + *

Credentials, bearer-token refresh and the vault configuration come from + * {@link BaseVaultClient}. The generated V2 client is created lazily and rebuilt whenever the + * vault URL or the effective HTTP settings change. + * + * @param the SDK's vault configuration type + */ +public abstract class BaseDetectController extends BaseVaultClient { + private static final String AUTHORIZATION_HEADER = "Authorization"; + private static final String BEARER_PREFIX = "Bearer "; + + private Integer commonTimeoutSeconds; + private Integer commonMaxRetries; + private Long commonInitialRetryDelayMillis; + private Long commonMaxRetryDelayMillis; + + private ClientOptions clientOptions; + private String clientOptionsKey; + private StringsV2Client stringsClient; + private FilesV2Client filesClient; + private GuardrailsV2Client guardrailsClient; + + protected BaseDetectController(V vaultConfig, BaseCredentials credentials) { + super(vaultConfig, credentials); + } + + // ------------------------------------------------------------------ + // Hooks for the concrete SDK controllers + // ------------------------------------------------------------------ + + /** + * @return the vault base URL requests are sent to, for example + * {@code https://.vault.skyflowapis.com} + * @throws SkyflowException if the URL cannot be determined + */ + protected abstract String resolveVaultUrl() throws SkyflowException; + + /** + * @return JSON sent in the {@code sky-metadata} header. Override to report SDK name and version. + */ + protected String getSdkMetrics() { + return "{}"; + } + + /** + * Resolves the bearer token or API key for the next call, refreshing it when expired. + * The default uses the credential chain in {@link BaseVaultClient}. + */ + protected String resolveToken() throws SkyflowException { + setBearerToken(this.vaultConfig.getCredentials()); + return this.token; + } + + /** @return overall call timeout in seconds, or {@code null} for the transport default */ + protected Integer effectiveTimeoutSeconds() { + return commonTimeoutSeconds; + } + + /** @return retries on retryable HTTP statuses, or {@code null} for the transport default */ + protected Integer effectiveMaxRetries() { + return commonMaxRetries; + } + + protected Long effectiveInitialRetryDelayMillis() { + return commonInitialRetryDelayMillis; + } + + protected Long effectiveMaxRetryDelayMillis() { + return commonMaxRetryDelayMillis; + } + + // ------------------------------------------------------------------ + // Lifecycle, driven by each SDK's Skyflow builder. Public because the builders live in + // com.skyflow while this class lives in com.skyflow.vault.controller; applications have + // no reason to call these directly. + // ------------------------------------------------------------------ + + /** Applies client-level credentials; vault-level credentials keep precedence. */ + public void setCommonCredentials(BaseCredentials commonCredentials) throws SkyflowException { + this.commonCredentials = commonCredentials; + prioritiseCredentials(this.vaultConfig.getCredentials()); + onCommonCredentialsUpdated(commonCredentials); + } + + /** + * Called after client-level credentials change. Subclasses that hold other clients (for + * example a V1 delegate) override this to forward the update; the default does nothing. + */ + protected void onCommonCredentialsUpdated(BaseCredentials commonCredentials) throws SkyflowException { + } + + /** Applies client-level timeout and retry settings; vault-level settings keep precedence. */ + public synchronized void setCommonHttpConfig(Integer timeoutSeconds, Integer maxRetries, + Long initialRetryDelayMillis, Long maxRetryDelayMillis) { + this.commonTimeoutSeconds = timeoutSeconds; + this.commonMaxRetries = maxRetries; + this.commonInitialRetryDelayMillis = initialRetryDelayMillis; + this.commonMaxRetryDelayMillis = maxRetryDelayMillis; + invalidateClients(); + } + + /** Replaces the vault configuration, for example after {@code updateVaultConfig}. */ + public synchronized void setVaultConfig(V vaultConfig) throws SkyflowException { + this.vaultConfig = vaultConfig; + invalidateClients(); + } + + /** Forces the vault URL to be resolved again on the next call. */ + public synchronized void refreshVaultUrl() throws SkyflowException { + invalidateClients(); + } + + /** Drops cached generated clients so the next call rebuilds them from current settings. */ + protected synchronized void invalidateClients() { + this.clientOptions = null; + this.clientOptionsKey = null; + this.stringsClient = null; + this.filesClient = null; + this.guardrailsClient = null; + } + + // ------------------------------------------------------------------ + // Detect V2 operations + // ------------------------------------------------------------------ + + /** + * Detects and de-identifies sensitive data in a string. Synchronous. + * + * @param request text plus exactly one of a stored configuration id or an inline configuration + * @return processed text, detected entities and metrics + * @throws SkyflowException on validation failure, credential failure or an API error + */ + public DeidentifyStringResponse deidentifyString(DeidentifyStringRequest request) throws SkyflowException { + LogUtil.printInfoLog(InfoLogs.DEIDENTIFY_STRING_TRIGGERED.getLog()); + LogUtil.printInfoLog(InfoLogs.VALIDATE_DEIDENTIFY_STRING_REQUEST.getLog()); + DetectValidations.validateDeidentifyStringRequest(request); + String token = resolveToken(); + DeidentifyStringRequestV2 apiRequest = + DetectRequestMapper.toDeidentifyStringRequest(request, this.vaultConfig.getVaultId()); + try { + DeidentifyStringResponseV2 apiResponse = stringsClient().deidentifyStringV2(apiRequest, requestOptions(token)); + LogUtil.printInfoLog(InfoLogs.DEIDENTIFY_STRING_REQUEST_RESOLVED.getLog()); + DeidentifyStringResponse response = DetectResponseMapper.toDeidentifyStringResponse(apiResponse); + LogUtil.printInfoLog(InfoLogs.DEIDENTIFY_STRING_SUCCESS.getLog()); + return response; + } catch (ApiClientApiException e) { + throw toSkyflowException(e, ErrorLogs.DEIDENTIFY_STRING_REQUEST_REJECTED); + } catch (ApiClientException e) { + LogUtil.printErrorLog(ErrorLogs.DEIDENTIFY_STRING_REQUEST_REJECTED.getLog()); + throw new SkyflowException(e); + } + } + + /** + * Replaces tokens in a string with their original values or a redacted rendering. Synchronous. + * + * @param request text containing tokens plus optional per-group redaction rules + * @return re-identified text and metrics + * @throws SkyflowException on validation failure, credential failure or an API error + */ + public ReidentifyStringResponse reidentifyString(ReidentifyStringRequest request) throws SkyflowException { + LogUtil.printInfoLog(InfoLogs.REIDENTIFY_STRING_TRIGGERED.getLog()); + LogUtil.printInfoLog(InfoLogs.VALIDATE_REIDENTIFY_STRING_REQUEST.getLog()); + DetectValidations.validateReidentifyStringRequest(request); + String token = resolveToken(); + ReidentifyStringRequestV2 apiRequest = + DetectRequestMapper.toReidentifyStringRequest(request, this.vaultConfig.getVaultId()); + try { + ReidentifyStringResponseV2 apiResponse = stringsClient().reidentifyStringV2(apiRequest, requestOptions(token)); + LogUtil.printInfoLog(InfoLogs.REIDENTIFY_STRING_REQUEST_RESOLVED.getLog()); + ReidentifyStringResponse response = DetectResponseMapper.toReidentifyStringResponse(apiResponse); + LogUtil.printInfoLog(InfoLogs.REIDENTIFY_STRING_SUCCESS.getLog()); + return response; + } catch (ApiClientApiException e) { + throw toSkyflowException(e, ErrorLogs.REIDENTIFY_STRING_REQUEST_REJECTED); + } catch (ApiClientException e) { + LogUtil.printErrorLog(ErrorLogs.REIDENTIFY_STRING_REQUEST_REJECTED.getLog()); + throw new SkyflowException(e); + } + } + + /** + * Retrieves the status and, once complete, the outputs of a Detect file run. + * + * @param request the run id returned by {@code deidentifyFile} + * @return status, output files, metrics and failure message + * @throws SkyflowException on validation failure, credential failure or an API error + */ + public GetRunResponse getRun(GetRunRequest request) throws SkyflowException { + LogUtil.printInfoLog(InfoLogs.GET_RUN_TRIGGERED.getLog()); + LogUtil.printInfoLog(InfoLogs.VALIDATE_GET_RUN_REQUEST.getLog()); + DetectValidations.validateGetRunRequest(request); + String token = resolveToken(); + String runId = request.getRunId().trim(); + GetRunV2Request apiRequest = GetRunV2Request.builder().vaultId(this.vaultConfig.getVaultId()).build(); + try { + DetectRunsResponseV2 apiResponse = filesClient().getRunV2(runId, apiRequest, requestOptions(token)); + LogUtil.printInfoLog(InfoLogs.GET_RUN_REQUEST_RESOLVED.getLog()); + GetRunResponse response = DetectResponseMapper.toGetRunResponse(runId, apiResponse); + LogUtil.printInfoLog(InfoLogs.GET_RUN_SUCCESS.getLog()); + return response; + } catch (ApiClientApiException e) { + throw toSkyflowException(e, ErrorLogs.GET_RUN_REQUEST_REJECTED); + } catch (ApiClientException e) { + LogUtil.printErrorLog(ErrorLogs.GET_RUN_REQUEST_REJECTED.getLog()); + throw new SkyflowException(e); + } + } + + /** + * Screens text for toxicity and denied topics. Synchronous. + * + * @param request text plus the checks to apply + * @return the overall validation result and the individual check outcomes + * @throws SkyflowException on validation failure, credential failure or an API error + */ + public CheckGuardrailsResponse checkGuardrails(CheckGuardrailsRequest request) throws SkyflowException { + LogUtil.printInfoLog(InfoLogs.CHECK_GUARDRAILS_TRIGGERED.getLog()); + LogUtil.printInfoLog(InfoLogs.VALIDATE_CHECK_GUARDRAILS_REQUEST.getLog()); + DetectValidations.validateCheckGuardrailsRequest(request); + String token = resolveToken(); + DetectGuardrailsRequestV2 apiRequest = + DetectRequestMapper.toCheckGuardrailsRequest(request, this.vaultConfig.getVaultId()); + try { + DetectGuardrailsResponseV2 apiResponse = guardrailsClient().checkGuardrailsV2(apiRequest, requestOptions(token)); + LogUtil.printInfoLog(InfoLogs.CHECK_GUARDRAILS_REQUEST_RESOLVED.getLog()); + CheckGuardrailsResponse response = DetectResponseMapper.toCheckGuardrailsResponse(apiResponse); + LogUtil.printInfoLog(InfoLogs.CHECK_GUARDRAILS_SUCCESS.getLog()); + return response; + } catch (ApiClientApiException e) { + throw toSkyflowException(e, ErrorLogs.CHECK_GUARDRAILS_REQUEST_REJECTED); + } catch (ApiClientException e) { + LogUtil.printErrorLog(ErrorLogs.CHECK_GUARDRAILS_REQUEST_REJECTED.getLog()); + throw new SkyflowException(e); + } + } + + // ------------------------------------------------------------------ + // Transport plumbing + // ------------------------------------------------------------------ + + /** + * @return client options for the current URL, vault id and HTTP settings, rebuilt when any change + */ + private synchronized ClientOptions clientOptions() throws SkyflowException { + String vaultUrl = resolveVaultUrl(); + String key = clientKey(vaultUrl); + if (clientOptions == null || !key.equals(clientOptionsKey)) { + clientOptions = buildClientOptions(vaultUrl); + clientOptionsKey = key; + stringsClient = null; + filesClient = null; + guardrailsClient = null; + } + return clientOptions; + } + + /** @return the generated strings client for the current settings */ + protected synchronized StringsV2Client stringsClient() throws SkyflowException { + ClientOptions options = clientOptions(); + if (stringsClient == null) { + stringsClient = new StringsV2Client(options); + } + return stringsClient; + } + + /** @return the generated guardrails client for the current settings */ + protected synchronized GuardrailsV2Client guardrailsClient() throws SkyflowException { + ClientOptions options = clientOptions(); + if (guardrailsClient == null) { + guardrailsClient = new GuardrailsV2Client(options); + } + return guardrailsClient; + } + + /** @return the generated files client for the current settings */ + protected synchronized FilesV2Client filesClient() throws SkyflowException { + ClientOptions options = clientOptions(); + if (filesClient == null) { + filesClient = new FilesV2Client(options); + } + return filesClient; + } + + private String clientKey(String vaultUrl) { + return vaultUrl + "|" + this.vaultConfig.getVaultId() + "|" + effectiveTimeoutSeconds() + "|" + + effectiveMaxRetries() + "|" + effectiveInitialRetryDelayMillis() + "|" + effectiveMaxRetryDelayMillis(); + } + + private ClientOptions buildClientOptions(String vaultUrl) { + ClientOptions.Builder builder = ClientOptions.builder().environment(Environment.custom(vaultUrl)); + Integer timeout = effectiveTimeoutSeconds(); + if (timeout != null) { + builder.timeout(timeout); + } + Integer maxRetries = effectiveMaxRetries(); + if (maxRetries != null) { + builder.maxRetries(maxRetries); + } + Long initialDelay = effectiveInitialRetryDelayMillis(); + if (initialDelay != null) { + builder.initialRetryDelayMillis(initialDelay); + } + Long maxDelay = effectiveMaxRetryDelayMillis(); + if (maxDelay != null) { + builder.maxRetryDelayMillis(maxDelay); + } + return builder.build(); + } + + private RequestOptions requestOptions(String token) { + return RequestOptions.builder() + .addHeader(AUTHORIZATION_HEADER, BEARER_PREFIX + token) + .addHeader(BaseConstants.SDK_METRICS_HEADER_KEY, getSdkMetrics()) + .build(); + } + + private static SkyflowException toSkyflowException(ApiClientApiException e, ErrorLogs errorLog) { + LogUtil.printErrorLog(errorLog.getLog()); + return new SkyflowException(e.statusCode(), e, e.headers(), bodyAsString(e.body())); + } + + static String bodyAsString(Object body) { + if (body == null) { + return null; + } + if (body instanceof String) { + return (String) body; + } + try { + return ObjectMappers.JSON_MAPPER.writeValueAsString(body); + } catch (Exception ex) { + return body.toString(); + } + } +} diff --git a/common/src/test/java/com/skyflow/BaseSkyflowTests.java b/common/src/test/java/com/skyflow/BaseSkyflowTests.java index cfd65ad5..45d5278c 100644 --- a/common/src/test/java/com/skyflow/BaseSkyflowTests.java +++ b/common/src/test/java/com/skyflow/BaseSkyflowTests.java @@ -5,7 +5,7 @@ import com.skyflow.enums.Env; import com.skyflow.enums.LogLevel; import com.skyflow.errors.ErrorCode; -import com.skyflow.errors.ErrorMessage; +import com.skyflow.errors.BaseErrorMessage; import com.skyflow.errors.SkyflowException; import com.skyflow.logs.ErrorLogs; import org.junit.Assert; @@ -45,7 +45,7 @@ public void testAddingExistingVaultConfigThrows() { Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); - Assert.assertEquals(ErrorMessage.VaultIdAlreadyInConfigList.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.VaultIdAlreadyInConfigList.getMessage(), e.getMessage()); } } @@ -57,7 +57,7 @@ public void testUpdatingNonExistentVaultConfigInBuilderThrows() { Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); - Assert.assertEquals(ErrorMessage.VaultIdNotInConfigList.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.VaultIdNotInConfigList.getMessage(), e.getMessage()); } } @@ -70,7 +70,7 @@ public void testUpdatingNonExistentVaultConfigInClientThrows() { Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); - Assert.assertEquals(ErrorMessage.VaultIdNotInConfigList.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.VaultIdNotInConfigList.getMessage(), e.getMessage()); } } @@ -81,7 +81,7 @@ public void testRemovingNonExistentVaultConfigInBuilderThrows() { Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); - Assert.assertEquals(ErrorMessage.VaultIdNotInConfigList.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.VaultIdNotInConfigList.getMessage(), e.getMessage()); } } @@ -104,7 +104,7 @@ public void testRemovingVaultConfigWithoutAddingThrows() { Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); - Assert.assertEquals(ErrorMessage.VaultIdNotInConfigList.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.VaultIdNotInConfigList.getMessage(), e.getMessage()); } } @@ -122,7 +122,7 @@ public void testVaultThrowsWhenNoConfigAdded() { Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); - Assert.assertEquals(ErrorMessage.VaultIdNotInConfigList.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.VaultIdNotInConfigList.getMessage(), e.getMessage()); } } @@ -137,7 +137,7 @@ public void testVaultByIdThrowsAfterRemoval() { Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); - Assert.assertEquals(ErrorMessage.VaultIdNotInConfigList.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.VaultIdNotInConfigList.getMessage(), e.getMessage()); } } @@ -163,7 +163,7 @@ public void testAddingInvalidSkyflowCredentialsThrows() { Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); - Assert.assertEquals(ErrorMessage.NoTokenGenerationMeansPassed.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.NoTokenGenerationMeansPassed.getMessage(), e.getMessage()); } } @@ -350,12 +350,12 @@ protected TestSkyflow self() { Object vault() throws SkyflowException { return resolveOrThrow(this.builder.vaultClientsMap, null, - ErrorLogs.VAULT_CONFIG_DOES_NOT_EXIST, ErrorMessage.VaultIdNotInConfigList); + ErrorLogs.VAULT_CONFIG_DOES_NOT_EXIST, BaseErrorMessage.VaultIdNotInConfigList); } Object vault(String vaultId) throws SkyflowException { return resolveOrThrow(this.builder.vaultClientsMap, vaultId, - ErrorLogs.VAULT_CONFIG_DOES_NOT_EXIST, ErrorMessage.VaultIdNotInConfigList); + ErrorLogs.VAULT_CONFIG_DOES_NOT_EXIST, BaseErrorMessage.VaultIdNotInConfigList); } private static class TestSkyflowClientBuilder extends BaseSkyflowClientBuilder { @@ -365,7 +365,7 @@ private static class TestSkyflowClientBuilder extends BaseSkyflowClientBuilder { }; + + private static GetRunResponse success() { + return success(DataSourceType.BASE64); + } + + private static GetRunResponse success(DataSourceType outputType) { + return new GetRunResponse("run-1", DetectRunStatus.SUCCESS, outputType, + Arrays.asList( + new FileOutput(Base64.getEncoder().encodeToString("redacted".getBytes()), + "REDACTED_FILE", FileDataFormat.TXT), + new FileOutput(Base64.getEncoder().encodeToString("[]".getBytes()), + "ENTITIES", FileDataFormat.JSON)), + new Metrics(null, 1, 8, null, null, null), null); + } + + @Test + public void withoutPollOptionsReturnsRunIdAndNeverFetches() throws Exception { + AtomicInteger fetches = new AtomicInteger(); + DeidentifyFileResponse response = DeidentifyFileFlow.complete( + DeidentifyFileRequest.builder().dataSource(DataSourceType.BASE64).value("x").build(), "run-1", + id -> { fetches.incrementAndGet(); return success(); }, NO_SLEEP); + Assert.assertEquals("run-1", response.getRunId()); + Assert.assertNull(response.getStatus()); + Assert.assertTrue(response.getOutput().isEmpty()); + Assert.assertEquals(0, fetches.get()); + } + + @Test + public void missingRunIdSkipsPollingEvenWhenRequested() throws Exception { + AtomicInteger fetches = new AtomicInteger(); + DeidentifyFileResponse response = DeidentifyFileFlow.complete( + DeidentifyFileRequest.builder().dataSource(DataSourceType.BASE64).value("x") + .pollOptions(PollOptions.builder().build()).build(), null, + id -> { fetches.incrementAndGet(); return success(); }, NO_SLEEP); + Assert.assertNull(response.getRunId()); + Assert.assertNull(response.getStatus()); + Assert.assertEquals(0, fetches.get()); + } + + @Test + public void polledRawRequestMergesRunButWritesNothing() throws Exception { + DeidentifyFileResponse response = DeidentifyFileFlow.complete( + DeidentifyFileRequest.builder().dataSource(DataSourceType.SKYFLOW_ID).value("rec") + .pollOptions(PollOptions.builder().build()).outputDirectory(folder.getRoot().getPath()).build(), + "run-1", id -> success(), NO_SLEEP); + Assert.assertEquals(DetectRunStatus.SUCCESS, response.getStatus()); + Assert.assertEquals(2, response.getOutput().size()); + Assert.assertEquals(Integer.valueOf(8), response.getMetrics().getCharacterCount()); + Assert.assertEquals(0, folder.getRoot().list().length); + } + + @Test + public void polledFileRequestWritesOutputsOnSuccess() throws Exception { + File input = folder.newFile("notes.txt"); + Files.write(input.toPath(), "hello".getBytes()); + File outDir = folder.newFolder("out"); + + DeidentifyFileResponse response = DeidentifyFileFlow.complete( + DeidentifyFileRequest.builder().file(input).pollOptions(PollOptions.builder().build()) + .outputDirectory(outDir.getPath()).build(), + "run-1", id -> success(), NO_SLEEP); + + Assert.assertEquals(DetectRunStatus.SUCCESS, response.getStatus()); + File written = new File(outDir, "processed-notes.txt"); + Assert.assertTrue(written.isFile()); + Assert.assertEquals("redacted", new String(Files.readAllBytes(written.toPath()))); + File entities = new File(outDir, "processed-entities-notes.json"); + Assert.assertTrue(entities.isFile()); + Assert.assertEquals("[]", new String(Files.readAllBytes(entities.toPath()))); + Assert.assertEquals(2, outDir.list().length); + } + + @Test + public void polledFileRequestWritesNothingForPresignedUrlOutputs() throws Exception { + File input = folder.newFile("notes.txt"); + File outDir = folder.newFolder("out"); + + DeidentifyFileResponse response = DeidentifyFileFlow.complete( + DeidentifyFileRequest.builder().file(input).pollOptions(PollOptions.builder().build()) + .outputDirectory(outDir.getPath()).build(), + "run-1", id -> success(DataSourceType.PRESIGNED_URL), NO_SLEEP); + + Assert.assertEquals(DetectRunStatus.SUCCESS, response.getStatus()); + Assert.assertEquals(DataSourceType.PRESIGNED_URL, response.getOutputType()); + Assert.assertEquals(2, response.getOutput().size()); + Assert.assertEquals(0, outDir.list().length); + } + + @Test + public void polledFileRequestWritesNothingWhenRunFailsOrIsPending() throws Exception { + File input = folder.newFile("notes.txt"); + File outDir = folder.newFolder("out"); + DeidentifyFileRequest request = DeidentifyFileRequest.builder().file(input) + .pollOptions(PollOptions.builder().maxAttempts(1).build()).outputDirectory(outDir.getPath()).build(); + + DeidentifyFileResponse failed = DeidentifyFileFlow.complete(request, "run-1", + id -> new GetRunResponse(id, DetectRunStatus.FAILED, null, Collections.emptyList(), null, "bad input"), NO_SLEEP); + Assert.assertEquals(DetectRunStatus.FAILED, failed.getStatus()); + Assert.assertEquals("bad input", failed.getMessage()); + + DeidentifyFileResponse pending = DeidentifyFileFlow.complete(request, "run-2", + id -> new GetRunResponse(id, DetectRunStatus.IN_PROGRESS, null, Collections.emptyList(), null, null), NO_SLEEP); + Assert.assertEquals("run-2", pending.getRunId()); + Assert.assertEquals(DetectRunStatus.IN_PROGRESS, pending.getStatus()); + Assert.assertEquals(0, outDir.list().length); + } +} diff --git a/common/src/test/java/com/skyflow/detect/internal/DeidentifyFileMapperTests.java b/common/src/test/java/com/skyflow/detect/internal/DeidentifyFileMapperTests.java new file mode 100644 index 00000000..41886c3c --- /dev/null +++ b/common/src/test/java/com/skyflow/detect/internal/DeidentifyFileMapperTests.java @@ -0,0 +1,146 @@ +package com.skyflow.detect.internal; + +import com.skyflow.detect.DataSourceType; +import com.skyflow.detect.DeidentifyFileRequest; +import com.skyflow.detect.DeidentifyFileResponse; +import com.skyflow.detect.Detect; +import com.skyflow.detect.DetectConfiguration; +import com.skyflow.detect.DetectRunStatus; +import com.skyflow.detect.Entity; +import com.skyflow.detect.EntityType; +import com.skyflow.detect.FileDataFormat; +import com.skyflow.detect.GetRunResponse; +import com.skyflow.errors.BaseErrorMessage; +import com.skyflow.errors.SkyflowException; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import com.skyflow.generated.detect.rest.resources.filesv2.requests.DeidentifyFileRequestV2; +import com.skyflow.generated.detect.rest.types.DetectRunsResponseV2; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.File; +import java.nio.file.Files; +import java.util.Base64; +import java.util.Collections; + +public class DeidentifyFileMapperTests { + + @Rule + public TemporaryFolder folder = new TemporaryFolder(); + + private static String wire(DeidentifyFileRequestV2 request) throws Exception { + return ObjectMappers.JSON_MAPPER.writeValueAsString(request); + } + + @Test + public void mapsRawValueRequest() throws Exception { + DeidentifyFileRequestV2 api = DetectRequestMapper.toDeidentifyFileRequest(DeidentifyFileRequest.builder() + .dataSource(DataSourceType.SKYFLOW_ID).value("rec-1").configurationId("cfg-1").build(), "vault-1"); + Assert.assertEquals("SKYFLOW_ID", api.getDataSource().toString()); + Assert.assertEquals("rec-1", api.getValue()); + Assert.assertEquals("cfg-1", api.getConfigurationId().get()); + Assert.assertFalse(api.getDataFormat().isPresent()); + Assert.assertFalse(api.getConfiguration().isPresent()); + String json = wire(api); + Assert.assertTrue(json, json.contains("\"dataSource\":\"SKYFLOW_ID\"")); + Assert.assertTrue(json, json.contains("\"configurationId\":\"cfg-1\"")); + Assert.assertFalse(json, json.contains("dataFormat")); + Assert.assertFalse(json, json.contains("\"configuration\"")); + } + + @Test + public void dataFormatIsSentLowerCase() throws Exception { + DeidentifyFileRequestV2 api = DetectRequestMapper.toDeidentifyFileRequest(DeidentifyFileRequest.builder() + .dataSource(DataSourceType.PRESIGNED_URL).value("https://s3/in").dataFormat(FileDataFormat.PDF).build(), "v"); + Assert.assertTrue(wire(api).contains("\"dataFormat\":\"pdf\"")); + } + + @Test + public void inlineConfigurationGetsVaultId() throws Exception { + DeidentifyFileRequestV2 api = DetectRequestMapper.toDeidentifyFileRequest(DeidentifyFileRequest.builder() + .dataSource(DataSourceType.BASE64).value("Zm9v") + .configuration(DetectConfiguration.builder().detect(Detect.builder().entities(Collections.singletonList( + Entity.builder().entityType(EntityType.NAME).build())).build()).build()) + .build(), "vault-9"); + String json = wire(api); + Assert.assertTrue(json, json.contains("\"vaultId\":\"vault-9\"")); + Assert.assertTrue(json, json.contains("\"entityType\":\"NAME\"")); + } + + @Test + public void fileRequestIsEncodedAndFormatInferred() throws Exception { + File input = folder.newFile("patient-notes.PDF"); + byte[] content = {1, 2, 3, 4, 5}; + Files.write(input.toPath(), content); + + DeidentifyFileRequestV2 api = DetectRequestMapper.toDeidentifyFileRequest( + DeidentifyFileRequest.builder().file(input).build(), "v"); + Assert.assertEquals("BASE64", api.getDataSource().toString()); + Assert.assertEquals(Base64.getEncoder().encodeToString(content), api.getValue()); + Assert.assertEquals("pdf", api.getDataFormat().get().toString()); + + DeidentifyFileRequestV2 explicit = DetectRequestMapper.toDeidentifyFileRequest( + DeidentifyFileRequest.builder().filePath(input.getPath()).dataFormat(FileDataFormat.TXT).build(), "v"); + Assert.assertEquals("txt", explicit.getDataFormat().get().toString()); + } + + @Test + public void localFileIsAlwaysSentAsBase64EvenIfDataSourceWasCleared() throws Exception { + File input = folder.newFile("a.txt"); + Files.write(input.toPath(), new byte[]{1}); + DeidentifyFileRequestV2 api = DetectRequestMapper.toDeidentifyFileRequest( + DeidentifyFileRequest.builder().file(input).dataSource(null).build(), "v"); + Assert.assertEquals("BASE64", api.getDataSource().toString()); + } + + @Test + public void unknownOrMissingExtensionLeavesFormatForTheServer() throws Exception { + for (String name : new String[]{"archive.heic", "README", "trailing."}) { + File input = folder.newFile(name); + Files.write(input.toPath(), new byte[]{9}); + DeidentifyFileRequestV2 api = DetectRequestMapper.toDeidentifyFileRequest( + DeidentifyFileRequest.builder().file(input).build(), "v"); + Assert.assertFalse(name, api.getDataFormat().isPresent()); + } + } + + @Test + public void unreadableFileIsReportedAsEncodeFailure() { + try { + DetectRequestMapper.toDeidentifyFileRequest( + DeidentifyFileRequest.builder().file(new File(folder.getRoot(), "nope.txt")).build(), "v"); + Assert.fail("expected SkyflowException"); + } catch (SkyflowException e) { + Assert.assertEquals(BaseErrorMessage.FailedToEncodeFile.getMessage(), e.getMessage()); + } + } + + @Test + public void submittedResponseCarriesOnlyRunId() { + DeidentifyFileResponse response = DetectResponseMapper.submittedDeidentifyFileResponse("run-7"); + Assert.assertEquals("run-7", response.getRunId()); + Assert.assertNull(response.getStatus()); + Assert.assertNull(response.getOutputType()); + Assert.assertTrue(response.getOutput().isEmpty()); + Assert.assertNull(response.getMetrics()); + Assert.assertNull(response.getMessage()); + } + + @Test + public void polledResponseMergesRunIdWithRun() throws Exception { + DetectRunsResponseV2 api = ObjectMappers.JSON_MAPPER.readValue( + "{\"status\":\"SUCCESS\",\"outputType\":\"BASE64\",\"output\":[{\"processedFile\":\"UERG\"," + + "\"processedFileType\":\"REDACTED_FILE\",\"processedFileExtension\":\"pdf\"}],\"metrics\":{\"pages\":2}}", + DetectRunsResponseV2.class); + GetRunResponse run = DetectResponseMapper.toGetRunResponse("run-7", api); + DeidentifyFileResponse response = DetectResponseMapper.toDeidentifyFileResponse("run-7", run); + Assert.assertEquals("run-7", response.getRunId()); + Assert.assertEquals(DetectRunStatus.SUCCESS, response.getStatus()); + Assert.assertEquals(DataSourceType.BASE64, response.getOutputType()); + Assert.assertEquals("UERG", response.getOutput().get(0).getProcessedFile()); + Assert.assertEquals(FileDataFormat.PDF, response.getOutput().get(0).getProcessedFileExtension()); + Assert.assertEquals(Integer.valueOf(2), response.getMetrics().getPages()); + } +} diff --git a/common/src/test/java/com/skyflow/detect/internal/DeidentifyFileValidationsTests.java b/common/src/test/java/com/skyflow/detect/internal/DeidentifyFileValidationsTests.java new file mode 100644 index 00000000..9f9a3f62 --- /dev/null +++ b/common/src/test/java/com/skyflow/detect/internal/DeidentifyFileValidationsTests.java @@ -0,0 +1,82 @@ +package com.skyflow.detect.internal; + +import com.skyflow.detect.DataSourceType; +import com.skyflow.detect.DeidentifyFileRequest; +import com.skyflow.detect.PollOptions; +import com.skyflow.errors.BaseErrorMessage; +import com.skyflow.errors.ErrorCode; +import com.skyflow.errors.SkyflowException; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.File; +import java.nio.file.Files; + +public class DeidentifyFileValidationsTests { + + @Rule + public TemporaryFolder folder = new TemporaryFolder(); + + private static void assertRejected(DeidentifyFileRequest request, BaseErrorMessage expected) { + try { + DetectValidations.validateDeidentifyFileRequest(request); + Assert.fail("expected SkyflowException: " + expected); + } catch (SkyflowException e) { + Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); + Assert.assertEquals(expected.getMessage(), e.getMessage()); + } + } + + private static DeidentifyFileRequest.DeidentifyFileRequestBuilder raw() { + return DeidentifyFileRequest.builder().dataSource(DataSourceType.BASE64).value("Zm9v"); + } + + @Test + public void rejectsNullRequest() { + assertRejected(null, BaseErrorMessage.DeidentifyFileRequestNull); + } + + @Test + public void rawValueRequiresDataSourceAndValue() throws SkyflowException { + assertRejected(DeidentifyFileRequest.builder().value("x").build(), BaseErrorMessage.InvalidDataSourceInDeidentifyFile); + assertRejected(DeidentifyFileRequest.builder().dataSource(DataSourceType.SKYFLOW_ID).build(), BaseErrorMessage.InvalidValueInDeidentifyFile); + assertRejected(raw().value(" ").build(), BaseErrorMessage.InvalidValueInDeidentifyFile); + DetectValidations.validateDeidentifyFileRequest(raw().build()); + DetectValidations.validateDeidentifyFileRequest(raw().configurationId("cfg").build()); + } + + @Test + public void fileRequestsNeedAnExistingFileAndNoValue() throws Exception { + File existing = folder.newFile("notes.txt"); + Files.write(existing.toPath(), "hello".getBytes("UTF-8")); + DetectValidations.validateDeidentifyFileRequest(DeidentifyFileRequest.builder().file(existing).build()); + DetectValidations.validateDeidentifyFileRequest(DeidentifyFileRequest.builder().filePath(existing.getPath()).build()); + + assertRejected(DeidentifyFileRequest.builder().file(existing).value("Zm9v").build(), BaseErrorMessage.MultipleFileSourcesInDeidentifyFile); + assertRejected(DeidentifyFileRequest.builder().filePath(new File(folder.getRoot(), "missing.txt").getPath()).build(), + BaseErrorMessage.FileNotFoundToDeidentify); + assertRejected(DeidentifyFileRequest.builder().file(folder.getRoot()).build(), BaseErrorMessage.FileNotFoundToDeidentify); + } + + @Test + public void pollOptionsBoundsAreEnforced() throws SkyflowException { + DetectValidations.validateDeidentifyFileRequest(raw().pollOptions(PollOptions.builder().build()).build()); + DetectValidations.validateDeidentifyFileRequest(raw().pollOptions(PollOptions.builder().waitTime(1).maxAttempts(1).build()).build()); + DetectValidations.validateDeidentifyFileRequest(raw().pollOptions(PollOptions.builder().waitTime(300).maxAttempts(23).build()).build()); + for (int bad : new int[]{0, -1, 301}) { + assertRejected(raw().pollOptions(PollOptions.builder().waitTime(bad).build()).build(), BaseErrorMessage.InvalidWaitTimeInPollOptions); + } + for (int bad : new int[]{0, -5, 24}) { + assertRejected(raw().pollOptions(PollOptions.builder().maxAttempts(bad).build()).build(), BaseErrorMessage.InvalidMaxAttemptsInPollOptions); + } + } + + @Test + public void outputDirectoryRequiresPollOptions() throws SkyflowException { + assertRejected(raw().outputDirectory("/tmp/out").build(), BaseErrorMessage.OutputDirectoryWithoutPollOptions); + DetectValidations.validateDeidentifyFileRequest(raw().outputDirectory(" ").build()); + DetectValidations.validateDeidentifyFileRequest(raw().outputDirectory("/tmp/out").pollOptions(PollOptions.builder().build()).build()); + } +} diff --git a/common/src/test/java/com/skyflow/detect/internal/DetectRequestMapperTests.java b/common/src/test/java/com/skyflow/detect/internal/DetectRequestMapperTests.java new file mode 100644 index 00000000..facad0ec --- /dev/null +++ b/common/src/test/java/com/skyflow/detect/internal/DetectRequestMapperTests.java @@ -0,0 +1,161 @@ +package com.skyflow.detect.internal; + +import com.skyflow.detect.CheckGuardrailsRequest; +import com.skyflow.detect.CustomType; +import com.skyflow.detect.DeidentificationType; +import com.skyflow.detect.DeidentifyStringRequest; +import com.skyflow.detect.Detect; +import com.skyflow.detect.DetectConfiguration; +import com.skyflow.detect.Entity; +import com.skyflow.detect.EntityType; +import com.skyflow.detect.Image; +import com.skyflow.detect.MaskingMethod; +import com.skyflow.detect.Media; +import com.skyflow.detect.RedactionLevel; +import com.skyflow.detect.RedactionType; +import com.skyflow.detect.ReidentifyStringRequest; +import com.skyflow.detect.ReturnEntitiesType; +import com.skyflow.detect.ShiftDates; +import com.skyflow.detect.Transformation; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import com.skyflow.generated.detect.rest.resources.guardrailsv2.requests.DetectGuardrailsRequestV2; +import com.skyflow.generated.detect.rest.resources.stringsv2.requests.DeidentifyStringRequestV2; +import com.skyflow.generated.detect.rest.resources.stringsv2.requests.ReidentifyStringRequestV2; +import com.skyflow.generated.detect.rest.types.DetectConfigV2; +import com.skyflow.detect.FileMapping; +import com.skyflow.detect.ObjectEntity; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; + +public class DetectRequestMapperTests { + + @Test + public void storedConfigurationSendsOnlyTextAndId() throws Exception { + DeidentifyStringRequestV2 mapped = DetectRequestMapper.toDeidentifyStringRequest( + DeidentifyStringRequest.builder().text("hello").configurationId("cfg-1").build(), "vault-1"); + Assert.assertEquals("hello", mapped.getText()); + Assert.assertEquals("cfg-1", mapped.getConfigurationId().get()); + Assert.assertFalse(mapped.getConfiguration().isPresent()); + String json = ObjectMappers.JSON_MAPPER.writeValueAsString(mapped); + Assert.assertTrue(json, json.contains("\"configurationId\":\"cfg-1\"")); + Assert.assertFalse("no vault id outside a configuration: " + json, json.contains("vaultId")); + Assert.assertFalse(json, json.contains("\"configuration\":")); + } + + @Test + public void inlineConfigurationIsScopedToVaultAndFullyMapped() throws Exception { + DetectConfiguration configuration = DetectConfiguration.builder() + .name("inline") + .detect(Detect.builder() + .entities(Arrays.asList( + Entity.builder().entityType(EntityType.NAME) + .deidentificationType(DeidentificationType.VAULT_TOKEN) + .destination("persons.name").build(), + Entity.builder().entityType(EntityType.DOB) + .deidentificationType(DeidentificationType.ENTITY_UNIQUE_COUNTER) + .transformation(Transformation.builder() + .shiftDates(ShiftDates.builder().minDays(10).maxDays(30).build()).build()) + .build())) + .restrict(Collections.singletonList("[0-9]{3}-[0-9]{2}-[0-9]{4}")) + .skip(Collections.singletonList("Skyflow")) + .customTypes(Collections.singletonList( + CustomType.builder().label("EMPLOYEE_ID").match(Collections.singletonList("EMP-[0-9]{6}")).build())) + .returnEntities(ReturnEntitiesType.ALL) + .build()) + .media(Media.builder().image(Image.builder().maskingMethod(MaskingMethod.BLUR).outputOcrText(true).build()).build()) + .build(); + + DeidentifyStringRequestV2 mapped = DetectRequestMapper.toDeidentifyStringRequest( + DeidentifyStringRequest.builder().text("hello").configuration(configuration).build(), "vault-1"); + + DetectConfigV2 config = mapped.getConfiguration().get(); + Assert.assertEquals("vault-1", config.getVaultId()); + Assert.assertEquals("inline", config.getName().get()); + Assert.assertFalse(mapped.getConfigurationId().isPresent()); + + String json = ObjectMappers.JSON_MAPPER.writeValueAsString(mapped); + Assert.assertEquals("vault id appears once, inside the inline configuration", + 1, json.split("\"vaultId\":\"vault-1\"", -1).length - 1); + for (String expected : new String[]{ + "\"vaultId\":\"vault-1\"", "\"entityType\":\"NAME\"", "\"deidentificationType\":\"VAULT_TOKEN\"", + "\"destination\":\"persons.name\"", "\"minDays\":10", "\"maxDays\":30", + "\"restrict\":[\"[0-9]{3}-[0-9]{2}-[0-9]{4}\"]", "\"skip\":[\"Skyflow\"]", + "\"label\":\"EMPLOYEE_ID\"", "\"returnEntities\":\"ALL\"", "\"maskingMethod\":\"BLUR\"", "\"outputOcrText\":true"}) { + Assert.assertTrue("missing " + expected + " in " + json, json.contains(expected)); + } + Assert.assertFalse("unset fields must be omitted: " + json, json.contains("\"description\"")); + } + + @Test + public void reidentifyStringCarriesVaultIdAndRedactionLevels() throws Exception { + ReidentifyStringRequestV2 mapped = DetectRequestMapper.toReidentifyStringRequest( + ReidentifyStringRequest.builder() + .text("[NAME_1]") + .redactionLevel(Arrays.asList( + RedactionLevel.builder().entityName(EntityType.NAME).redactionType(RedactionType.MASKED).build(), + RedactionLevel.builder().tokenGroupName("group").redactionPattern("pattern").build())) + .build(), "vault-1"); + Assert.assertEquals("vault-1", mapped.getVaultId()); + Assert.assertEquals("[NAME_1]", mapped.getText()); + Assert.assertEquals(2, mapped.getRedactionLevel().get().size()); + String json = ObjectMappers.JSON_MAPPER.writeValueAsString(mapped); + Assert.assertTrue(json, json.contains("\"entityName\":\"NAME\"")); + Assert.assertTrue(json, json.contains("\"redactionType\":\"MASKED\"")); + Assert.assertTrue(json, json.contains("\"tokenGroupName\":\"group\"")); + Assert.assertTrue(json, json.contains("\"redactionPattern\":\"pattern\"")); + } + + @Test + public void reidentifyStringWithoutRedactionLevelsOmitsField() throws Exception { + ReidentifyStringRequestV2 mapped = DetectRequestMapper.toReidentifyStringRequest( + ReidentifyStringRequest.builder().text("[NAME_1]").build(), "vault-1"); + Assert.assertFalse(mapped.getRedactionLevel().isPresent()); + Assert.assertFalse(ObjectMappers.JSON_MAPPER.writeValueAsString(mapped).contains("redactionLevel")); + } + + @Test + public void everyPublicEntityTypeMapsToAGeneratedValue() { + for (EntityType type : EntityType.values()) { + Entity entity = Entity.builder().entityType(type).build(); + Assert.assertEquals(type.name(), DetectRequestMapper.toEntity(entity).getEntityType().get().toString()); + RedactionLevel level = RedactionLevel.builder().entityName(type).redactionType(RedactionType.DEFAULT).build(); + Assert.assertEquals(type.name(), + DetectRequestMapper.toRedactionLevels(Collections.singletonList(level)).get(0).getEntityName().get().toString()); + } + } + + @Test + public void checkGuardrailsCarriesVaultIdAndOnlySetOptions() throws Exception { + DetectGuardrailsRequestV2 full = DetectRequestMapper.toCheckGuardrailsRequest( + CheckGuardrailsRequest.builder().text("t").checkToxicity(false).denyTopics(Collections.singletonList("x")).build(), "vault-1"); + Assert.assertEquals("vault-1", full.getVaultId()); + Assert.assertEquals("t", full.getText()); + Assert.assertEquals(Boolean.FALSE, full.getCheckToxicity().get()); + Assert.assertEquals(Collections.singletonList("x"), full.getDenyTopics().get()); + + DetectGuardrailsRequestV2 minimal = DetectRequestMapper.toCheckGuardrailsRequest( + CheckGuardrailsRequest.builder().text("t").build(), "vault-1"); + Assert.assertFalse(minimal.getCheckToxicity().isPresent()); + Assert.assertFalse(minimal.getDenyTopics().isPresent()); + } + + @Test + public void nullElementsInConfigurationListsAreSkipped() throws Exception { + com.skyflow.generated.detect.rest.types.DetectConfigV2 api = DetectRequestMapper.toDetectConfig( + DetectConfiguration.builder() + .detect(Detect.builder() + .entities(Arrays.asList(null, Entity.builder().entityType(EntityType.NAME).build())) + .objectEntities(Arrays.asList((ObjectEntity) null)) + .customTypes(Arrays.asList(null, CustomType.builder().label("ID").build())) + .build()) + .fileMapping(Arrays.asList(null, FileMapping.builder().source("in").build())) + .build(), "v"); + Assert.assertEquals(1, api.getDetect().get().getEntities().get().size()); + Assert.assertTrue(api.getDetect().get().getObjectEntities().get().isEmpty()); + Assert.assertEquals(1, api.getDetect().get().getCustomTypes().get().size()); + Assert.assertEquals(1, api.getFileMapping().get().size()); + } +} diff --git a/common/src/test/java/com/skyflow/detect/internal/DetectResponseMapperTests.java b/common/src/test/java/com/skyflow/detect/internal/DetectResponseMapperTests.java new file mode 100644 index 00000000..0a4ab894 --- /dev/null +++ b/common/src/test/java/com/skyflow/detect/internal/DetectResponseMapperTests.java @@ -0,0 +1,140 @@ +package com.skyflow.detect.internal; + +import com.skyflow.detect.CheckGuardrailsResponse; +import com.skyflow.detect.DataSourceType; +import com.skyflow.detect.DeidentifyStringResponse; +import com.skyflow.detect.DetectRunStatus; +import com.skyflow.detect.FileDataFormat; +import com.skyflow.detect.GetRunResponse; +import com.skyflow.detect.GuardrailsValidation; +import com.skyflow.detect.DetectedEntity; +import com.skyflow.detect.ReidentifyStringResponse; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import com.skyflow.generated.detect.rest.types.DeidentifyStringResponseV2; +import com.skyflow.generated.detect.rest.types.DetectGuardrailsResponseV2; +import com.skyflow.generated.detect.rest.types.DetectRunsResponseV2; +import com.skyflow.generated.detect.rest.types.ReidentifyStringResponseV2; +import org.junit.Assert; +import org.junit.Test; + +public class DetectResponseMapperTests { + + @Test + public void mapsFullDeidentifyStringResponse() throws Exception { + String json = "{\"processedText\":\"My name is [NAME_1].\",\"entities\":[{\"token\":\"NAME_1\",\"value\":\"\"," + + "\"location\":{\"startIndex\":11,\"endIndex\":19,\"startIndexProcessed\":11,\"endIndexProcessed\":19}," + + "\"entityType\":\"NAME\",\"entityScores\":{\"NAME\":0.9152}}]," + + "\"metrics\":{\"size\":0.05,\"wordCount\":4,\"characterCount\":20,\"duration\":0.2}}"; + DeidentifyStringResponseV2 api = ObjectMappers.JSON_MAPPER.readValue(json, DeidentifyStringResponseV2.class); + + DeidentifyStringResponse response = DetectResponseMapper.toDeidentifyStringResponse(api); + Assert.assertEquals("My name is [NAME_1].", response.getProcessedText()); + Assert.assertEquals(1, response.getEntities().size()); + DetectedEntity entity = response.getEntities().get(0); + Assert.assertEquals("NAME_1", entity.getToken()); + Assert.assertEquals("", entity.getValue()); + Assert.assertEquals("NAME", entity.getEntityType()); + Assert.assertEquals(Integer.valueOf(11), entity.getLocation().getStartIndex()); + Assert.assertEquals(Integer.valueOf(19), entity.getLocation().getEndIndexProcessed()); + Assert.assertEquals(0.9152, entity.getEntityScores().get("NAME"), 1e-9); + Assert.assertEquals(Integer.valueOf(4), response.getMetrics().getWordCount()); + Assert.assertEquals(Integer.valueOf(20), response.getMetrics().getCharacterCount()); + Assert.assertEquals(0.05, response.getMetrics().getSize(), 1e-9); + Assert.assertNull(response.getMetrics().getPages()); + } + + @Test + public void emptyResponseYieldsEmptyEntitiesNotNull() throws Exception { + DeidentifyStringResponseV2 api = ObjectMappers.JSON_MAPPER.readValue("{}", DeidentifyStringResponseV2.class); + DeidentifyStringResponse response = DetectResponseMapper.toDeidentifyStringResponse(api); + Assert.assertNull(response.getProcessedText()); + Assert.assertNotNull(response.getEntities()); + Assert.assertTrue(response.getEntities().isEmpty()); + Assert.assertNull(response.getMetrics()); + } + + @Test + public void nullListElementsFromTheApiAreSkipped() throws Exception { + DeidentifyStringResponseV2 strings = ObjectMappers.JSON_MAPPER.readValue( + "{\"entities\":[null,{\"token\":\"NAME_1\"},null]}", DeidentifyStringResponseV2.class); + Assert.assertEquals(1, DetectResponseMapper.toDeidentifyStringResponse(strings).getEntities().size()); + + DetectRunsResponseV2 run = ObjectMappers.JSON_MAPPER.readValue( + "{\"status\":\"SUCCESS\",\"output\":[null,{\"processedFile\":\"x\"}]}", DetectRunsResponseV2.class); + Assert.assertEquals(1, DetectResponseMapper.toGetRunResponse("r", run).getOutput().size()); + } + + @Test + public void guardrailsResponseWithoutValidationMapsToUnknown() throws Exception { + DetectGuardrailsResponseV2 api = ObjectMappers.JSON_MAPPER.readValue("{}", DetectGuardrailsResponseV2.class); + CheckGuardrailsResponse response = DetectResponseMapper.toCheckGuardrailsResponse(api); + Assert.assertNull(response.getText()); + Assert.assertEquals(GuardrailsValidation.UNKNOWN, response.getValidation()); + Assert.assertNull(response.getToxic()); + Assert.assertNull(response.getDeniedTopic()); + } + + @Test + public void entityWithoutScoresGetsEmptyMap() throws Exception { + DeidentifyStringResponseV2 api = ObjectMappers.JSON_MAPPER.readValue( + "{\"entities\":[{\"token\":\"NAME_1\"}]}", DeidentifyStringResponseV2.class); + DetectedEntity entity = DetectResponseMapper.toDeidentifyStringResponse(api).getEntities().get(0); + Assert.assertNotNull(entity.getEntityScores()); + Assert.assertTrue(entity.getEntityScores().isEmpty()); + Assert.assertNull(entity.getLocation()); + } + + @Test + public void mapsReidentifyStringResponse() throws Exception { + ReidentifyStringResponseV2 api = ObjectMappers.JSON_MAPPER.readValue( + "{\"processedText\":\"My name is John Doe.\",\"metrics\":{\"wordCount\":5}}", ReidentifyStringResponseV2.class); + ReidentifyStringResponse response = DetectResponseMapper.toReidentifyStringResponse(api); + Assert.assertEquals("My name is John Doe.", response.getProcessedText()); + Assert.assertEquals(Integer.valueOf(5), response.getMetrics().getWordCount()); + } + + @Test + public void mapsCompletedRun() throws Exception { + DetectRunsResponseV2 api = ObjectMappers.JSON_MAPPER.readValue( + "{\"status\":\"SUCCESS\",\"outputType\":\"PRESIGNED_URL\",\"output\":[{\"processedFile\":\"https://x/y.pdf\"," + + "\"processedFileType\":\"REDACTED_FILE\",\"processedFileExtension\":\"pdf\"}],\"metrics\":{\"pages\":2}}", + DetectRunsResponseV2.class); + GetRunResponse run = DetectResponseMapper.toGetRunResponse("run-1", api); + Assert.assertEquals("run-1", run.getRunId()); + Assert.assertEquals(DetectRunStatus.SUCCESS, run.getStatus()); + Assert.assertEquals(DataSourceType.PRESIGNED_URL, run.getOutputType()); + Assert.assertEquals("https://x/y.pdf", run.getOutput().get(0).getProcessedFile()); + Assert.assertEquals(FileDataFormat.PDF, run.getOutput().get(0).getProcessedFileExtension()); + Assert.assertEquals(Integer.valueOf(2), run.getMetrics().getPages()); + } + + @Test + public void runWithoutStatusIsUnknownAndUnknownWireValuesAreTolerated() throws Exception { + GetRunResponse empty = DetectResponseMapper.toGetRunResponse("r", + ObjectMappers.JSON_MAPPER.readValue("{}", DetectRunsResponseV2.class)); + Assert.assertEquals(DetectRunStatus.UNKNOWN, empty.getStatus()); + Assert.assertTrue(empty.getOutput().isEmpty()); + Assert.assertNull(empty.getOutputType()); + + GetRunResponse odd = DetectResponseMapper.toGetRunResponse("r", ObjectMappers.JSON_MAPPER.readValue( + "{\"status\":\"NEW_STATE\",\"outputType\":\"FTP\",\"output\":[{\"processedFileExtension\":\"heic\"}]}", + DetectRunsResponseV2.class)); + Assert.assertEquals(DetectRunStatus.UNKNOWN, odd.getStatus()); + Assert.assertNull(odd.getOutputType()); + Assert.assertNull(odd.getOutput().get(0).getProcessedFileExtension()); + } + + @Test + public void mapsGuardrailsResponseAndToleratesUnknownValidation() throws Exception { + CheckGuardrailsResponse failed = DetectResponseMapper.toCheckGuardrailsResponse(ObjectMappers.JSON_MAPPER.readValue( + "{\"text\":\"t\",\"validation\":\"FAILED\",\"toxic\":true}", DetectGuardrailsResponseV2.class)); + Assert.assertEquals("t", failed.getText()); + Assert.assertEquals(GuardrailsValidation.FAILED, failed.getValidation()); + Assert.assertEquals(Boolean.TRUE, failed.getToxic()); + Assert.assertNull(failed.getDeniedTopic()); + + CheckGuardrailsResponse odd = DetectResponseMapper.toCheckGuardrailsResponse(ObjectMappers.JSON_MAPPER.readValue( + "{\"text\":\"t\",\"validation\":\"MAYBE\"}", DetectGuardrailsResponseV2.class)); + Assert.assertEquals(GuardrailsValidation.UNKNOWN, odd.getValidation()); + } +} diff --git a/common/src/test/java/com/skyflow/detect/internal/DetectValidationsTests.java b/common/src/test/java/com/skyflow/detect/internal/DetectValidationsTests.java new file mode 100644 index 00000000..dd154096 --- /dev/null +++ b/common/src/test/java/com/skyflow/detect/internal/DetectValidationsTests.java @@ -0,0 +1,138 @@ +package com.skyflow.detect.internal; + +import com.skyflow.detect.CheckGuardrailsRequest; +import com.skyflow.detect.DeidentifyStringRequest; +import com.skyflow.detect.DetectConfiguration; +import com.skyflow.detect.EntityType; +import com.skyflow.detect.GetRunRequest; +import com.skyflow.detect.RedactionLevel; +import com.skyflow.detect.RedactionType; +import com.skyflow.detect.ReidentifyStringRequest; +import com.skyflow.errors.BaseErrorMessage; +import com.skyflow.errors.ErrorCode; +import com.skyflow.errors.SkyflowException; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; + +public class DetectValidationsTests { + + private static void assertInvalid(SkyflowException e, BaseErrorMessage expected) { + Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); + Assert.assertEquals(expected.getMessage(), e.getMessage()); + } + + @Test + public void deidentifyStringRejectsNullRequest() { + try { + DetectValidations.validateDeidentifyStringRequest(null); + Assert.fail("expected SkyflowException"); + } catch (SkyflowException e) { + assertInvalid(e, BaseErrorMessage.DeidentifyStringRequestNull); + } + } + + @Test + public void deidentifyStringRejectsBlankText() { + try { + DetectValidations.validateDeidentifyStringRequest( + DeidentifyStringRequest.builder().text(" ").configurationId("cfg").build()); + Assert.fail("expected SkyflowException"); + } catch (SkyflowException e) { + assertInvalid(e, BaseErrorMessage.InvalidTextInDeidentifyString); + } + } + + @Test + public void deidentifyStringDoesNotConstrainConfigurationSources() throws SkyflowException { + // configurationId and configuration are both optional on the API: none, either or both pass through + DetectValidations.validateDeidentifyStringRequest(DeidentifyStringRequest.builder().text("hello").build()); + DetectValidations.validateDeidentifyStringRequest(DeidentifyStringRequest.builder().text("hello") + .configurationId("cfg").configuration(DetectConfiguration.builder().build()).build()); + DetectValidations.validateDeidentifyStringRequest( + DeidentifyStringRequest.builder().text("hello").configurationId("cfg").build()); + DetectValidations.validateDeidentifyStringRequest( + DeidentifyStringRequest.builder().text("hello") + .configuration(DetectConfiguration.builder().build()).build()); + } + + @Test + public void reidentifyStringRejectsNullAndBlank() { + try { + DetectValidations.validateReidentifyStringRequest(null); + Assert.fail("expected SkyflowException"); + } catch (SkyflowException e) { + assertInvalid(e, BaseErrorMessage.ReidentifyStringRequestNull); + } + try { + DetectValidations.validateReidentifyStringRequest(ReidentifyStringRequest.builder().text("").build()); + Assert.fail("expected SkyflowException"); + } catch (SkyflowException e) { + assertInvalid(e, BaseErrorMessage.InvalidTextInReidentifyString); + } + } + + @Test + public void reidentifyStringAcceptsValidRedactionLevels() throws SkyflowException { + DetectValidations.validateReidentifyStringRequest(ReidentifyStringRequest.builder() + .text("[NAME_1]") + .redactionLevel(Arrays.asList( + RedactionLevel.builder().entityName(EntityType.NAME).redactionType(RedactionType.MASKED).build(), + RedactionLevel.builder().tokenGroupName("group").redactionPattern("pattern").build())) + .build()); + DetectValidations.validateReidentifyStringRequest(ReidentifyStringRequest.builder().text("[NAME_1]").build()); + } + + @Test + public void reidentifyStringPassesAnyRedactionLevelShapeThrough() throws SkyflowException { + // The API marks every RedactionLevel field optional; the server applies its own rules. + DetectValidations.validateReidentifyStringRequest(ReidentifyStringRequest.builder().text("[NAME_1]") + .redactionLevel(Arrays.asList( + RedactionLevel.builder().build(), + RedactionLevel.builder().tokenGroupName("g").build(), + RedactionLevel.builder().entityName(EntityType.NAME).redactionType(RedactionType.MASKED).redactionPattern("p").build(), + null)) + .build()); + } + + @Test + public void getRunRequiresRunId() throws SkyflowException { + try { + DetectValidations.validateGetRunRequest(null); + Assert.fail("expected SkyflowException"); + } catch (SkyflowException e) { + assertInvalid(e, BaseErrorMessage.GetRunRequestNull); + } + try { + DetectValidations.validateGetRunRequest(GetRunRequest.builder().runId(" ").build()); + Assert.fail("expected SkyflowException"); + } catch (SkyflowException e) { + assertInvalid(e, BaseErrorMessage.InvalidRunIdInGetRun); + } + DetectValidations.validateGetRunRequest(GetRunRequest.builder().runId("run-1").build()); + } + + @Test + public void checkGuardrailsRequiresOnlyText() throws SkyflowException { + try { + DetectValidations.validateCheckGuardrailsRequest(null); + Assert.fail("expected SkyflowException"); + } catch (SkyflowException e) { + assertInvalid(e, BaseErrorMessage.CheckGuardrailsRequestNull); + } + try { + DetectValidations.validateCheckGuardrailsRequest(CheckGuardrailsRequest.builder().build()); + Assert.fail("expected SkyflowException"); + } catch (SkyflowException e) { + assertInvalid(e, BaseErrorMessage.InvalidTextInCheckGuardrails); + } + // denyTopics is optional on the API and its entries are not inspected by the SDK. + DetectValidations.validateCheckGuardrailsRequest( + CheckGuardrailsRequest.builder().text("hi").denyTopics(Arrays.asList("politics", "", null)).build()); + DetectValidations.validateCheckGuardrailsRequest(CheckGuardrailsRequest.builder().text("hi").build()); + DetectValidations.validateCheckGuardrailsRequest( + CheckGuardrailsRequest.builder().text("hi").checkToxicity(true).denyTopics(Collections.singletonList("politics")).build()); + } +} diff --git a/common/src/test/java/com/skyflow/detect/internal/FileOutputWriterTests.java b/common/src/test/java/com/skyflow/detect/internal/FileOutputWriterTests.java new file mode 100644 index 00000000..70297c59 --- /dev/null +++ b/common/src/test/java/com/skyflow/detect/internal/FileOutputWriterTests.java @@ -0,0 +1,274 @@ +package com.skyflow.detect.internal; + +import com.skyflow.detect.DataSourceType; +import com.skyflow.detect.FileDataFormat; +import com.skyflow.detect.FileOutput; +import com.skyflow.errors.SkyflowException; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.File; +import java.nio.file.Files; +import java.util.Arrays; +import java.util.Base64; +import java.util.Collections; +import java.util.List; + +public class FileOutputWriterTests { + + @Rule + public TemporaryFolder folder = new TemporaryFolder(); + + private static String b64(String text) { + return Base64.getEncoder().encodeToString(text.getBytes()); + } + + @Test + public void writesEachOutputWithV1StyleNames() throws Exception { + List written = FileOutputWriter.write(Arrays.asList( + new FileOutput(b64("%PDF"), "REDACTED_FILE", FileDataFormat.PDF), + new FileOutput(b64("[]"), "ENTITIES", null)), + DataSourceType.BASE64, "invoice.pdf", folder.getRoot().getPath()); + + Assert.assertEquals(2, written.size()); + Assert.assertEquals("processed-invoice.pdf", written.get(0).getName()); + Assert.assertEquals("processed-entities-invoice.json", written.get(1).getName()); + Assert.assertEquals("%PDF", new String(Files.readAllBytes(written.get(0).toPath()))); + Assert.assertEquals("[]", new String(Files.readAllBytes(written.get(1).toPath()))); + Assert.assertEquals(folder.getRoot(), written.get(0).getParentFile()); + } + + @Test + public void entityOutputsCarryTheirTypeInTheName() throws Exception { + List written = FileOutputWriter.write(Arrays.asList( + new FileOutput(b64("a"), "ENTITIES", FileDataFormat.JSON), + new FileOutput(b64("b"), "OBJECT_ENTITIES", FileDataFormat.JSON)), + DataSourceType.BASE64, "scan.png", folder.getRoot().getPath()); + Assert.assertEquals("processed-entities-scan.json", written.get(0).getName()); + Assert.assertEquals("processed-object-entities-scan.json", written.get(1).getName()); + } + + @Test + public void jsonInputDoesNotCollideWithItsEntitiesOutput() throws Exception { + List written = FileOutputWriter.write(Arrays.asList( + new FileOutput(b64("{}"), "REDACTED_TEXT", FileDataFormat.JSON), + new FileOutput(b64("[]"), "ENTITIES", FileDataFormat.JSON)), + DataSourceType.BASE64, "data.json", folder.getRoot().getPath()); + Assert.assertEquals("processed-data.json", written.get(0).getName()); + Assert.assertEquals("processed-entities-data.json", written.get(1).getName()); + } + + @Test + public void nameCollisionsGetTheFileTypeInserted() throws Exception { + List written = FileOutputWriter.write(Arrays.asList( + new FileOutput(b64("a"), "REDACTED_FILE", FileDataFormat.TXT), + new FileOutput(b64("b"), "REDACTED_TEXT", FileDataFormat.TXT)), + DataSourceType.BASE64, "notes.txt", folder.getRoot().getPath()); + Assert.assertEquals("processed-notes.txt", written.get(0).getName()); + Assert.assertEquals("processed-notes.redacted_text.txt", written.get(1).getName()); + } + + @Test + public void createsMissingOutputDirectory() throws Exception { + File nested = new File(folder.getRoot(), "a/b/c"); + List written = FileOutputWriter.write( + Collections.singletonList(new FileOutput(b64("x"), "REDACTED_TEXT", FileDataFormat.TXT)), + DataSourceType.BASE64, "notes.txt", nested.getPath()); + Assert.assertTrue(nested.isDirectory()); + Assert.assertEquals(new File(nested, "processed-notes.txt"), written.get(0)); + } + + @Test + public void presignedUrlOutputsAreNotWritten() throws Exception { + List written = FileOutputWriter.write( + Collections.singletonList(new FileOutput("https://s3/out", "REDACTED_FILE", FileDataFormat.PDF)), + DataSourceType.PRESIGNED_URL, "invoice.pdf", folder.getRoot().getPath()); + Assert.assertTrue(written.isEmpty()); + Assert.assertEquals(0, folder.getRoot().list().length); + } + + @Test + public void emptyOrNullOutputsWriteNothing() throws Exception { + Assert.assertTrue(FileOutputWriter.write(null, DataSourceType.BASE64, "x.txt", folder.getRoot().getPath()).isEmpty()); + Assert.assertTrue(FileOutputWriter.write(Collections.emptyList(), DataSourceType.BASE64, "x.txt", folder.getRoot().getPath()).isEmpty()); + Assert.assertTrue(FileOutputWriter.write(Collections.singletonList(new FileOutput(null, "ENTITIES", null)), + DataSourceType.BASE64, "x.txt", folder.getRoot().getPath()).isEmpty()); + } + + @Test + public void invalidBase64IsReportedAsWriteFailure() { + try { + FileOutputWriter.write(Collections.singletonList(new FileOutput("not base64!", "REDACTED_FILE", FileDataFormat.TXT)), + DataSourceType.BASE64, "x.txt", folder.getRoot().getPath()); + Assert.fail("expected SkyflowException"); + } catch (SkyflowException e) { + Assert.assertTrue(e.getMessage(), e.getMessage().contains("processed-x.txt")); + } + } + + // ---- naming branches ------------------------------------------------------------------- + + @Test + public void nullOrEmptyTypeFallsBackToPlainName() throws Exception { + List written = FileOutputWriter.write(Arrays.asList( + new FileOutput(b64("a"), null, FileDataFormat.PDF), + new FileOutput(b64("b"), "", FileDataFormat.TXT)), + DataSourceType.BASE64, "invoice.pdf", folder.getRoot().getPath()); + Assert.assertEquals("processed-invoice.pdf", written.get(0).getName()); + Assert.assertEquals("processed-invoice.txt", written.get(1).getName()); + } + + @Test + public void nonEntityOutputWithoutExtensionGetsBin() throws Exception { + List written = FileOutputWriter.write( + Collections.singletonList(new FileOutput(b64("x"), "REDACTED_TRANSCRIPTION", null)), + DataSourceType.BASE64, "call.mp3", folder.getRoot().getPath()); + Assert.assertEquals("processed-call.bin", written.get(0).getName()); + } + + @Test + public void entityOutputHonoursAnExplicitExtension() throws Exception { + List written = FileOutputWriter.write( + Collections.singletonList(new FileOutput(b64("x"), "ENTITIES", FileDataFormat.TXT)), + DataSourceType.BASE64, "notes.txt", folder.getRoot().getPath()); + Assert.assertEquals("processed-entities-notes.txt", written.get(0).getName()); + } + + @Test + public void entityTypeMatchIsCaseInsensitive() throws Exception { + List written = FileOutputWriter.write( + Collections.singletonList(new FileOutput(b64("x"), "entities", null)), + DataSourceType.BASE64, "notes.txt", folder.getRoot().getPath()); + Assert.assertEquals("processed-entities-notes.json", written.get(0).getName()); + } + + @Test + public void duplicateEntityOutputsGetTheTypeInsertedOnCollision() throws Exception { + List written = FileOutputWriter.write(Arrays.asList( + new FileOutput(b64("a"), "ENTITIES", FileDataFormat.JSON), + new FileOutput(b64("b"), "ENTITIES", FileDataFormat.JSON)), + DataSourceType.BASE64, "scan.png", folder.getRoot().getPath()); + Assert.assertEquals("processed-entities-scan.json", written.get(0).getName()); + Assert.assertEquals("processed-entities-scan.entities.json", written.get(1).getName()); + } + + @Test + public void collisionWithoutATypeUsesARunningNumber() throws Exception { + List written = FileOutputWriter.write(Arrays.asList( + new FileOutput(b64("a"), null, FileDataFormat.TXT), + new FileOutput(b64("b"), null, FileDataFormat.TXT), + new FileOutput(b64("c"), null, FileDataFormat.TXT)), + DataSourceType.BASE64, "notes.txt", folder.getRoot().getPath()); + Assert.assertEquals("processed-notes.txt", written.get(0).getName()); + Assert.assertEquals("processed-notes.1.txt", written.get(1).getName()); + Assert.assertEquals("processed-notes.2.txt", written.get(2).getName()); + } + + @Test + public void inputNamesWithoutAnExtensionOrWithLeadingDotAreKept() throws Exception { + List plain = FileOutputWriter.write( + Collections.singletonList(new FileOutput(b64("x"), "ENTITIES", null)), + DataSourceType.BASE64, "README", folder.getRoot().getPath()); + Assert.assertEquals("processed-entities-README.json", plain.get(0).getName()); + + List dotted = FileOutputWriter.write( + Collections.singletonList(new FileOutput(b64("x"), "REDACTED_TEXT", FileDataFormat.TXT)), + DataSourceType.BASE64, ".notes", folder.getRoot().getPath()); + Assert.assertEquals("processed-.notes.txt", dotted.get(0).getName()); + } + + @Test + public void missingInputNameFallsBackToOutput() throws Exception { + List written = FileOutputWriter.write(Arrays.asList( + new FileOutput(b64("a"), "REDACTED_FILE", FileDataFormat.PDF), + new FileOutput(b64("b"), "ENTITIES", null)), + DataSourceType.BASE64, null, folder.getRoot().getPath()); + Assert.assertEquals("processed-output.pdf", written.get(0).getName()); + Assert.assertEquals("processed-entities-output.json", written.get(1).getName()); + } + + // ---- directory and output-type branches --------------------------------------------------- + + @Test + public void nullOutputTypeWritesNothing() throws Exception { + List written = FileOutputWriter.write( + Collections.singletonList(new FileOutput(b64("x"), "REDACTED_FILE", FileDataFormat.PDF)), + null, "invoice.pdf", folder.getRoot().getPath()); + Assert.assertTrue(written.isEmpty()); + Assert.assertEquals(0, folder.getRoot().list().length); + } + + @Test + public void nullElementsInTheOutputListAreSkipped() throws Exception { + List written = FileOutputWriter.write(Arrays.asList( + null, + new FileOutput(b64("x"), "REDACTED_TEXT", FileDataFormat.TXT)), + DataSourceType.BASE64, "notes.txt", folder.getRoot().getPath()); + Assert.assertEquals(1, written.size()); + Assert.assertEquals("processed-notes.txt", written.get(0).getName()); + } + + @Test + public void blankOutputDirectoryWritesToTheWorkingDirectory() throws Exception { + File expected = new File("processed-writer-cwd-test.txt"); + try { + List written = FileOutputWriter.write( + Collections.singletonList(new FileOutput(b64("x"), "REDACTED_TEXT", FileDataFormat.TXT)), + DataSourceType.BASE64, "writer-cwd-test.txt", " "); + Assert.assertEquals(expected, written.get(0)); + Assert.assertNull(written.get(0).getParentFile()); + Assert.assertTrue(expected.isFile()); + } finally { + Files.deleteIfExists(expected.toPath()); + } + } + + @Test + public void outputDirectoryThatIsAFileIsReportedAsWriteFailure() throws Exception { + File notADirectory = folder.newFile("blocker"); + try { + FileOutputWriter.write( + Collections.singletonList(new FileOutput(b64("x"), "REDACTED_TEXT", FileDataFormat.TXT)), + DataSourceType.BASE64, "notes.txt", notADirectory.getPath()); + Assert.fail("expected SkyflowException"); + } catch (SkyflowException e) { + Assert.assertTrue(e.getMessage(), e.getMessage().contains(notADirectory.getPath())); + } + } + + @Test + public void existingFilesAreOverwritten() throws Exception { + File target = new File(folder.getRoot(), "processed-notes.txt"); + Files.write(target.toPath(), "old".getBytes()); + FileOutputWriter.write( + Collections.singletonList(new FileOutput(b64("new"), "REDACTED_TEXT", FileDataFormat.TXT)), + DataSourceType.BASE64, "notes.txt", folder.getRoot().getPath()); + Assert.assertEquals("new", new String(Files.readAllBytes(target.toPath()))); + } + + @Test + public void aLaterFailureLeavesEarlierFilesOnDisk() throws Exception { + try { + FileOutputWriter.write(Arrays.asList( + new FileOutput(b64("ok"), "REDACTED_FILE", FileDataFormat.PDF), + new FileOutput("not base64!", "ENTITIES", null)), + DataSourceType.BASE64, "invoice.pdf", folder.getRoot().getPath()); + Assert.fail("expected SkyflowException"); + } catch (SkyflowException e) { + Assert.assertTrue(e.getMessage(), e.getMessage().contains("processed-entities-invoice.json")); + } + Assert.assertTrue(new File(folder.getRoot(), "processed-invoice.pdf").isFile()); + Assert.assertFalse(new File(folder.getRoot(), "processed-entities-invoice.json").exists()); + } + + @Test + public void nameHelpers() { + Assert.assertEquals("notes", FileOutputWriter.stripExtension("notes.txt")); + Assert.assertEquals("archive.tar", FileOutputWriter.stripExtension("archive.tar.gz")); + Assert.assertEquals("README", FileOutputWriter.stripExtension("README")); + Assert.assertEquals(".hidden", FileOutputWriter.stripExtension(".hidden")); + Assert.assertEquals("output", FileOutputWriter.stripExtension(null)); + } +} diff --git a/common/src/test/java/com/skyflow/detect/polling/RunPollerTests.java b/common/src/test/java/com/skyflow/detect/polling/RunPollerTests.java new file mode 100644 index 00000000..1030eec6 --- /dev/null +++ b/common/src/test/java/com/skyflow/detect/polling/RunPollerTests.java @@ -0,0 +1,162 @@ +package com.skyflow.detect.polling; + +import com.skyflow.detect.DataSourceType; +import com.skyflow.detect.DetectRunStatus; +import com.skyflow.detect.GetRunResponse; +import com.skyflow.detect.PollOptions; +import com.skyflow.errors.SkyflowException; +import org.junit.Assert; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +public class RunPollerTests { + + private static GetRunResponse run(DetectRunStatus status) { + return new GetRunResponse("run-1", status, status == DetectRunStatus.SUCCESS ? DataSourceType.BASE64 : null, + Collections.emptyList(), null, status == DetectRunStatus.FAILED ? "boom" : null); + } + + /** Serves a scripted sequence of statuses and records every sleep instead of sleeping. */ + private static final class Script implements RunPoller.RunFetcher, RunPoller.Sleeper { + final Iterator statuses; + final List sleepsMillis = new ArrayList<>(); + int fetches; + + Script(DetectRunStatus... statuses) { + this.statuses = Arrays.asList(statuses).iterator(); + } + + @Override + public GetRunResponse fetch(String runId) { + Assert.assertEquals("run-1", runId); + fetches++; + Assert.assertTrue("fetched more times than scripted", statuses.hasNext()); + return run(statuses.next()); + } + + @Override + public void sleep(long millis) { + sleepsMillis.add(millis); + } + } + + private static PollOptions options(Integer waitTime, Integer maxAttempts) { + return PollOptions.builder().waitTime(waitTime).maxAttempts(maxAttempts).build(); + } + + @Test + public void terminalStatusesStopImmediatelyWithoutSleeping() throws SkyflowException { + for (DetectRunStatus terminal : new DetectRunStatus[]{DetectRunStatus.SUCCESS, DetectRunStatus.FAILED, DetectRunStatus.UNKNOWN}) { + Script script = new Script(terminal); + GetRunResponse result = RunPoller.poll("run-1", options(null, null), script, script); + Assert.assertEquals(terminal, result.getStatus()); + Assert.assertEquals(1, script.fetches); + Assert.assertTrue(script.sleepsMillis.isEmpty()); + } + } + + @Test + public void nonTerminalStatusesKeepPollingWithExponentialBackoff() throws SkyflowException { + Script script = new Script(DetectRunStatus.QUEUED, DetectRunStatus.QUEUED, DetectRunStatus.IN_PROGRESS, + DetectRunStatus.IN_PROGRESS, DetectRunStatus.SUCCESS); + GetRunResponse result = RunPoller.poll("run-1", options(null, null), script, script); + Assert.assertEquals(DetectRunStatus.SUCCESS, result.getStatus()); + Assert.assertEquals(5, script.fetches); + Assert.assertEquals(Arrays.asList(1000L, 2000L, 4000L, 8000L), script.sleepsMillis); + } + + @Test + public void defaultWaitTimeIsSpentExactlyThenRunIdIsReturned() throws SkyflowException { + DetectRunStatus[] forever = new DetectRunStatus[30]; + Arrays.fill(forever, DetectRunStatus.IN_PROGRESS); + Script script = new Script(forever); + GetRunResponse result = RunPoller.poll("run-1", options(null, null), script, script); + Assert.assertEquals(DetectRunStatus.IN_PROGRESS, result.getStatus()); + // 1+2+4+8+16 = 31, then the 32 s step is trimmed to 29 so the total is exactly 60. + Assert.assertEquals(Arrays.asList(1000L, 2000L, 4000L, 8000L, 16000L, 29000L), script.sleepsMillis); + Assert.assertEquals(7, script.fetches); + } + + @Test + public void delayPlateausAt32SecondsForLongWaits() throws SkyflowException { + DetectRunStatus[] forever = new DetectRunStatus[30]; + Arrays.fill(forever, DetectRunStatus.QUEUED); + Script script = new Script(forever); + RunPoller.poll("run-1", options(300, null), script, script); + // 1+2+4+8+16 = 31, then eight 32 s steps reach 287, and the last step is trimmed to 13 so the total is exactly 300. + Assert.assertEquals(Arrays.asList(1000L, 2000L, 4000L, 8000L, 16000L, 32000L, 32000L, 32000L, 32000L, 32000L, + 32000L, 32000L, 32000L, 13000L), script.sleepsMillis); + Assert.assertEquals(15, script.fetches); + } + + @Test + public void maxAttemptsCapsLookupsBeforeWaitTime() throws SkyflowException { + Script script = new Script(DetectRunStatus.IN_PROGRESS, DetectRunStatus.IN_PROGRESS, DetectRunStatus.IN_PROGRESS); + GetRunResponse result = RunPoller.poll("run-1", options(300, 3), script, script); + Assert.assertEquals(DetectRunStatus.IN_PROGRESS, result.getStatus()); + Assert.assertEquals(3, script.fetches); + Assert.assertEquals(Arrays.asList(1000L, 2000L), script.sleepsMillis); + } + + @Test + public void singleAttemptNeverSleeps() throws SkyflowException { + Script script = new Script(DetectRunStatus.QUEUED); + RunPoller.poll("run-1", options(null, 1), script, script); + Assert.assertEquals(1, script.fetches); + Assert.assertTrue(script.sleepsMillis.isEmpty()); + } + + @Test + public void shortWaitTimeTrimsTheFirstSleep() throws SkyflowException { + Script script = new Script(DetectRunStatus.QUEUED, DetectRunStatus.QUEUED); + RunPoller.poll("run-1", options(1, null), script, script); + Assert.assertEquals(Collections.singletonList(1000L), script.sleepsMillis); + Assert.assertEquals(2, script.fetches); + } + + @Test + public void interruptionReturnsLastRunAndRestoresFlag() throws SkyflowException { + Script script = new Script(DetectRunStatus.IN_PROGRESS, DetectRunStatus.SUCCESS); + RunPoller.Sleeper interrupting = millis -> { + throw new InterruptedException(); + }; + try { + GetRunResponse result = RunPoller.poll("run-1", options(null, null), script, interrupting); + Assert.assertEquals(DetectRunStatus.IN_PROGRESS, result.getStatus()); + Assert.assertEquals(1, script.fetches); + Assert.assertTrue(Thread.currentThread().isInterrupted()); + } finally { + Thread.interrupted(); // clear so later tests are unaffected + } + } + + @Test + public void nullRunOrStatusFromFetcherIsTerminalUnknown() throws SkyflowException { + GetRunResponse fromNull = RunPoller.poll("run-1", options(null, null), runId -> null, millis -> { }); + Assert.assertEquals("run-1", fromNull.getRunId()); + Assert.assertEquals(DetectRunStatus.UNKNOWN, fromNull.getStatus()); + Assert.assertTrue(fromNull.getOutput().isEmpty()); + + GetRunResponse fromNullStatus = RunPoller.poll("run-1", options(null, null), + runId -> new GetRunResponse(runId, null, null, Collections.emptyList(), null, null), millis -> { }); + Assert.assertEquals(DetectRunStatus.UNKNOWN, fromNullStatus.getStatus()); + } + + @Test + public void fetchFailurePropagates() { + RunPoller.RunFetcher failing = runId -> { + throw new SkyflowException(404, "run not found"); + }; + try { + RunPoller.poll("run-1", options(null, null), failing, millis -> { }); + Assert.fail("expected SkyflowException"); + } catch (SkyflowException e) { + Assert.assertEquals(404, e.getHttpCode()); + } + } +} diff --git a/common/src/test/java/com/skyflow/serviceaccount/util/BearerTokenTests.java b/common/src/test/java/com/skyflow/serviceaccount/util/BearerTokenTests.java index 141548f8..9557b48b 100644 --- a/common/src/test/java/com/skyflow/serviceaccount/util/BearerTokenTests.java +++ b/common/src/test/java/com/skyflow/serviceaccount/util/BearerTokenTests.java @@ -3,7 +3,7 @@ import com.google.gson.Gson; import com.google.gson.JsonObject; import com.skyflow.errors.ErrorCode; -import com.skyflow.errors.ErrorMessage; +import com.skyflow.errors.BaseErrorMessage; import com.skyflow.errors.SkyflowException; import com.skyflow.generated.auth.rest.core.ApiClientException; import com.skyflow.generated.auth.rest.types.V1GetAuthTokenResponse; @@ -102,7 +102,7 @@ public void testEmptyCredentialsFilePath() { } catch (SkyflowException e) { Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); Assert.assertEquals( - BaseUtils.parameterizedString(ErrorMessage.FileNotFound.getMessage(), ""), e.getMessage() + BaseUtils.parameterizedString(BaseErrorMessage.FileNotFound.getMessage(), ""), e.getMessage() ); } } @@ -117,7 +117,7 @@ public void testInvalidFilePath() { } catch (SkyflowException e) { Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); Assert.assertEquals( - BaseUtils.parameterizedString(ErrorMessage.FileNotFound.getMessage(), invalidFilePath), + BaseUtils.parameterizedString(BaseErrorMessage.FileNotFound.getMessage(), invalidFilePath), e.getMessage() ); } @@ -133,7 +133,7 @@ public void testInvalidCredentialsFile() { } catch (SkyflowException e) { Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); Assert.assertEquals( - BaseUtils.parameterizedString(ErrorMessage.FileInvalidJson.getMessage(), invalidJsonFilePath), + BaseUtils.parameterizedString(BaseErrorMessage.FileInvalidJson.getMessage(), invalidJsonFilePath), e.getMessage() ); } @@ -147,7 +147,7 @@ public void testEmptyCredentialsString() { Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); - Assert.assertEquals(ErrorMessage.InvalidCredentials.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.InvalidCredentials.getMessage(), e.getMessage()); } } @@ -159,7 +159,7 @@ public void testInvalidCredentialsString() { Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); - Assert.assertEquals(ErrorMessage.CredentialsStringInvalidJson.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.CredentialsStringInvalidJson.getMessage(), e.getMessage()); } } @@ -174,7 +174,7 @@ public void testNoPrivateKeyInCredentialsForCredentials() { Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); - Assert.assertEquals(ErrorMessage.MissingPrivateKey.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.MissingPrivateKey.getMessage(), e.getMessage()); } } @@ -188,7 +188,7 @@ public void testNoClientIDInCredentialsForCredentials() { Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); - Assert.assertEquals(ErrorMessage.MissingClientId.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.MissingClientId.getMessage(), e.getMessage()); } } @@ -202,7 +202,7 @@ public void testNoKeyIDInCredentialsForCredentials() { Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); - Assert.assertEquals(ErrorMessage.MissingKeyId.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.MissingKeyId.getMessage(), e.getMessage()); } } @@ -216,7 +216,7 @@ public void testNoTokenURIInCredentialsForCredentials() { Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); - Assert.assertEquals(ErrorMessage.MissingTokenUri.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.MissingTokenUri.getMessage(), e.getMessage()); } } @@ -230,7 +230,7 @@ public void testInvalidPrivateKeyInCredentialsForCredentials() { Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); - Assert.assertEquals(ErrorMessage.JwtInvalidFormat.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.JwtInvalidFormat.getMessage(), e.getMessage()); } } @@ -243,7 +243,7 @@ public void testInvalidKeySpecInCredentialsForCredentials() { Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); - Assert.assertEquals(ErrorMessage.InvalidKeySpec.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.InvalidKeySpec.getMessage(), e.getMessage()); } } @@ -257,7 +257,7 @@ public void testInvalidTokenURIInCredentialsForCredentials() throws SkyflowExcep Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); - Assert.assertEquals(ErrorMessage.InvalidTokenUri.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.InvalidTokenUri.getMessage(), e.getMessage()); } } @@ -274,7 +274,7 @@ public void testBearerTokenWithNewFormCredentialKeys() { Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); // InvalidKeySpec confirms all credential fields were resolved — failure is at RSA parsing, not field lookup Assert.assertEquals( - BaseUtils.parameterizedString(ErrorMessage.InvalidKeySpec.getMessage(), BaseConstants.SDK_PREFIX), + BaseUtils.parameterizedString(BaseErrorMessage.InvalidKeySpec.getMessage(), BaseConstants.SDK_PREFIX), e.getMessage()); } } @@ -371,7 +371,7 @@ public void testExtractAccessTokenMissingThrowsSkyflowException() throws Excepti Assert.assertTrue(e.getCause() instanceof SkyflowException); SkyflowException skyflowException = (SkyflowException) e.getCause(); Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), skyflowException.getHttpCode()); - Assert.assertEquals(ErrorMessage.MissingAccessToken.getMessage(), skyflowException.getMessage()); + Assert.assertEquals(BaseErrorMessage.MissingAccessToken.getMessage(), skyflowException.getMessage()); } } diff --git a/common/src/test/java/com/skyflow/serviceaccount/util/SignedDataTokensTests.java b/common/src/test/java/com/skyflow/serviceaccount/util/SignedDataTokensTests.java index dab89e53..790de436 100644 --- a/common/src/test/java/com/skyflow/serviceaccount/util/SignedDataTokensTests.java +++ b/common/src/test/java/com/skyflow/serviceaccount/util/SignedDataTokensTests.java @@ -4,7 +4,7 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.skyflow.errors.ErrorCode; -import com.skyflow.errors.ErrorMessage; +import com.skyflow.errors.BaseErrorMessage; import com.skyflow.errors.SkyflowException; import com.skyflow.utils.BaseConstants; import com.skyflow.utils.BaseUtils; @@ -94,7 +94,7 @@ public void testEmptyCredentialsFilePath() { Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); - Assert.assertEquals(BaseUtils.parameterizedString(ErrorMessage.FileNotFound.getMessage(), ""), e.getMessage()); + Assert.assertEquals(BaseUtils.parameterizedString(BaseErrorMessage.FileNotFound.getMessage(), ""), e.getMessage()); } } @@ -108,7 +108,7 @@ public void testInvalidFilePath() { } catch (SkyflowException e) { Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); Assert.assertEquals( - BaseUtils.parameterizedString(ErrorMessage.FileNotFound.getMessage(), invalidFilePath), + BaseUtils.parameterizedString(BaseErrorMessage.FileNotFound.getMessage(), invalidFilePath), e.getMessage()); } } @@ -123,7 +123,7 @@ public void testInvalidCredentialsFile() { } catch (SkyflowException e) { Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); Assert.assertEquals( - BaseUtils.parameterizedString(ErrorMessage.FileInvalidJson.getMessage(), invalidJsonFilePath), + BaseUtils.parameterizedString(BaseErrorMessage.FileInvalidJson.getMessage(), invalidJsonFilePath), e.getMessage() ); } @@ -138,7 +138,7 @@ public void testEmptyCredentialsString() { } catch (SkyflowException e) { Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); // InvalidCredentials has no %s1 placeholder, so no extra arg is passed here. - Assert.assertEquals(ErrorMessage.InvalidCredentials.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.InvalidCredentials.getMessage(), e.getMessage()); } catch (Exception e) { Assert.fail(INVALID_EXCEPTION_THROWN + ": " + e); } @@ -153,7 +153,7 @@ public void testInvalidCredentialsString() { } catch (SkyflowException e) { Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); // CredentialsStringInvalidJson has no %s1 placeholder, so no extra arg is passed here. - Assert.assertEquals(ErrorMessage.CredentialsStringInvalidJson.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.CredentialsStringInvalidJson.getMessage(), e.getMessage()); } } @@ -167,7 +167,7 @@ public void testNoPrivateKeyInCredentials() { Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); - Assert.assertEquals(ErrorMessage.MissingPrivateKey.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.MissingPrivateKey.getMessage(), e.getMessage()); } } @@ -181,7 +181,7 @@ public void testNoClientIDInCredentials() { Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); - Assert.assertEquals(ErrorMessage.MissingClientId.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.MissingClientId.getMessage(), e.getMessage()); } } @@ -195,7 +195,7 @@ public void testNoKeyIDInCredentials() { Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); - Assert.assertEquals(ErrorMessage.MissingKeyId.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.MissingKeyId.getMessage(), e.getMessage()); } } @@ -209,7 +209,7 @@ public void testInvalidPrivateKeyInCredentials() { Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); - Assert.assertEquals(ErrorMessage.JwtInvalidFormat.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.JwtInvalidFormat.getMessage(), e.getMessage()); } } @@ -225,7 +225,7 @@ public void testInvalidKeySpecInCredentials() { Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); - Assert.assertEquals(ErrorMessage.InvalidKeySpec.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.InvalidKeySpec.getMessage(), e.getMessage()); } } @@ -245,7 +245,7 @@ public void testSignedDataTokensWithNewFormCredentialKeys() { } catch (SkyflowException e) { // Failure at RSA key parsing (not field lookup) confirms new-form keys clientId/keyId were found Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); - Assert.assertEquals(ErrorMessage.InvalidKeySpec.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.InvalidKeySpec.getMessage(), e.getMessage()); } } diff --git a/common/src/test/java/com/skyflow/utils/BaseUtilsTests.java b/common/src/test/java/com/skyflow/utils/BaseUtilsTests.java index bc1bd216..257e9d9b 100644 --- a/common/src/test/java/com/skyflow/utils/BaseUtilsTests.java +++ b/common/src/test/java/com/skyflow/utils/BaseUtilsTests.java @@ -4,7 +4,7 @@ import com.skyflow.config.Credentials; import com.skyflow.enums.Env; import com.skyflow.errors.ErrorCode; -import com.skyflow.errors.ErrorMessage; +import com.skyflow.errors.BaseErrorMessage; import com.skyflow.errors.SkyflowException; import org.junit.Assert; import org.junit.BeforeClass; @@ -105,7 +105,7 @@ public void testGenerateBearerTokenWithCredentialsFile() { } catch (SkyflowException e) { Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); Assert.assertEquals( - BaseUtils.parameterizedString(ErrorMessage.FileNotFound.getMessage(), filePath), + BaseUtils.parameterizedString(BaseErrorMessage.FileNotFound.getMessage(), filePath), e.getMessage() ); } @@ -122,7 +122,7 @@ public void testGenerateBearerTokenWithCredentialsString() { Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); - Assert.assertEquals(ErrorMessage.CredentialsStringInvalidJson.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.CredentialsStringInvalidJson.getMessage(), e.getMessage()); } } @@ -166,7 +166,7 @@ public void testGenerateBearerTokenWithCredentialsFileAndMapContext() { } catch (SkyflowException e) { Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); Assert.assertEquals( - BaseUtils.parameterizedString(ErrorMessage.FileNotFound.getMessage(), filePath), + BaseUtils.parameterizedString(BaseErrorMessage.FileNotFound.getMessage(), filePath), e.getMessage() ); } @@ -187,7 +187,7 @@ public void testGenerateBearerTokenWithCredentialsStringAndMapContext() { Assert.fail("Map context should not cause a ClassCastException"); } catch (SkyflowException e) { Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); - Assert.assertEquals(ErrorMessage.CredentialsStringInvalidJson.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.CredentialsStringInvalidJson.getMessage(), e.getMessage()); } } @@ -205,7 +205,7 @@ public void testGenerateBearerTokenWithContextNeitherStringNorMap() { Assert.fail("Non String/Map context should not cause a ClassCastException"); } catch (SkyflowException e) { Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); - Assert.assertEquals(ErrorMessage.CredentialsStringInvalidJson.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.CredentialsStringInvalidJson.getMessage(), e.getMessage()); } } @@ -216,7 +216,7 @@ public void testGetPrivateKeyFromPemWithMissingHeader() { Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); - Assert.assertEquals(ErrorMessage.JwtInvalidFormat.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.JwtInvalidFormat.getMessage(), e.getMessage()); } } @@ -230,7 +230,7 @@ public void testGetPrivateKeyFromPemWithInvalidBase64Body() { Assert.fail("Invalid base64 content should be wrapped into a SkyflowException, not thrown raw"); } catch (SkyflowException e) { Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); - Assert.assertEquals(ErrorMessage.InvalidKeySpec.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.InvalidKeySpec.getMessage(), e.getMessage()); } } diff --git a/common/src/test/java/com/skyflow/vault/controller/BaseDetectControllerTests.java b/common/src/test/java/com/skyflow/vault/controller/BaseDetectControllerTests.java new file mode 100644 index 00000000..4b267bb3 --- /dev/null +++ b/common/src/test/java/com/skyflow/vault/controller/BaseDetectControllerTests.java @@ -0,0 +1,385 @@ +package com.skyflow.vault.controller; + +import com.skyflow.config.BaseVaultConfig; +import com.skyflow.detect.DeidentifyStringRequest; +import com.skyflow.detect.DeidentifyStringResponse; +import com.skyflow.detect.Detect; +import com.skyflow.detect.DetectConfiguration; +import com.skyflow.detect.Entity; +import com.skyflow.detect.CheckGuardrailsRequest; +import com.skyflow.detect.CheckGuardrailsResponse; +import com.skyflow.detect.DataSourceType; +import com.skyflow.detect.DetectRunStatus; +import com.skyflow.detect.EntityType; +import com.skyflow.detect.FileDataFormat; +import com.skyflow.detect.GetRunRequest; +import com.skyflow.detect.GetRunResponse; +import com.skyflow.detect.GuardrailsValidation; +import com.skyflow.detect.RedactionLevel; +import com.skyflow.detect.ReidentifyStringRequest; +import com.skyflow.detect.ReidentifyStringResponse; +import com.skyflow.enums.Env; +import com.skyflow.errors.BaseErrorMessage; +import com.skyflow.errors.ErrorCode; +import com.skyflow.errors.SkyflowException; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import okhttp3.mockwebserver.RecordedRequest; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.concurrent.atomic.AtomicInteger; + +public class BaseDetectControllerTests { + + /** Minimal SDK controller: fixed URL, counted tokens, no credential chain. */ + private static final class TestDetectController extends BaseDetectController { + private String vaultUrl; + private final AtomicInteger tokenCalls = new AtomicInteger(); + private SkyflowException tokenFailure; + + TestDetectController(BaseVaultConfig config, String vaultUrl) { + super(config, null); + this.vaultUrl = vaultUrl; + } + + @Override + protected String resolveVaultUrl() { + return vaultUrl; + } + + @Override + protected String getSdkMetrics() { + return "{\"sdk_name_version\":\"test\"}"; + } + + @Override + protected String resolveToken() throws SkyflowException { + if (tokenFailure != null) { + throw tokenFailure; + } + return "token-" + tokenCalls.incrementAndGet(); + } + } + + private MockWebServer server; + private TestDetectController controller; + + private static BaseVaultConfig config(String vaultId) { + BaseVaultConfig config = new BaseVaultConfig(); + config.setVaultId(vaultId); + config.setClusterId("cluster"); + config.setEnv(Env.PROD); + return config; + } + + @Before + public void setUp() throws Exception { + server = new MockWebServer(); + server.start(); + controller = new TestDetectController(config("vault-1"), server.url("").toString()); + controller.setCommonHttpConfig(null, 0, null, null); + } + + @After + public void tearDown() throws Exception { + server.shutdown(); + } + + @Test + public void deidentifyStringSendsScopedRequestAndMapsResponse() throws Exception { + server.enqueue(new MockResponse().setResponseCode(200).setHeader("Content-Type", "application/json") + .setBody("{\"processedText\":\"My name is [NAME_1].\",\"entities\":[{\"token\":\"NAME_1\",\"entityType\":\"NAME\"," + + "\"location\":{\"startIndex\":11,\"endIndex\":19}}],\"metrics\":{\"wordCount\":4}}")); + + DeidentifyStringResponse response = controller.deidentifyString(DeidentifyStringRequest.builder() + .text("My name is John Doe.") + .configuration(DetectConfiguration.builder() + .detect(Detect.builder().entities(Collections.singletonList( + Entity.builder().entityType(EntityType.NAME).build())).build()) + .build()) + .build()); + + Assert.assertEquals("My name is [NAME_1].", response.getProcessedText()); + Assert.assertEquals("NAME_1", response.getEntities().get(0).getToken()); + Assert.assertEquals(Integer.valueOf(4), response.getMetrics().getWordCount()); + + RecordedRequest recorded = server.takeRequest(); + Assert.assertEquals("POST", recorded.getMethod()); + Assert.assertEquals("/v2/detect/deidentify/string", recorded.getPath()); + Assert.assertEquals("Bearer token-1", recorded.getHeader("Authorization")); + Assert.assertEquals("{\"sdk_name_version\":\"test\"}", recorded.getHeader("sky-metadata")); + String body = recorded.getBody().readUtf8(); + Assert.assertTrue(body, body.contains("\"text\":\"My name is John Doe.\"")); + Assert.assertTrue(body, body.contains("\"vaultId\":\"vault-1\"")); + Assert.assertTrue(body, body.contains("\"entityType\":\"NAME\"")); + Assert.assertFalse(body, body.contains("configurationId")); + } + + @Test + public void reidentifyStringSendsVaultIdInBodyAndRefreshesTokenPerCall() throws Exception { + server.enqueue(new MockResponse().setResponseCode(200).setBody("{\"processedText\":\"My name is John Doe.\"}")); + server.enqueue(new MockResponse().setResponseCode(200).setBody("{\"processedText\":\"My name is J**n D*e.\"}")); + + ReidentifyStringResponse first = controller.reidentifyString( + ReidentifyStringRequest.builder().text("My name is [NAME_1].").build()); + ReidentifyStringResponse second = controller.reidentifyString(ReidentifyStringRequest.builder() + .text("My name is [NAME_1].") + .redactionLevel(Collections.singletonList( + RedactionLevel.builder().tokenGroupName("group").redactionPattern("mask").build())) + .build()); + + Assert.assertEquals("My name is John Doe.", first.getProcessedText()); + Assert.assertEquals("My name is J**n D*e.", second.getProcessedText()); + + RecordedRequest r1 = server.takeRequest(); + Assert.assertEquals("/v2/detect/reidentify/string", r1.getPath()); + Assert.assertEquals("Bearer token-1", r1.getHeader("Authorization")); + String body1 = r1.getBody().readUtf8(); + Assert.assertTrue(body1, body1.contains("\"vaultId\":\"vault-1\"")); + Assert.assertFalse(body1, body1.contains("redactionLevel")); + + RecordedRequest r2 = server.takeRequest(); + Assert.assertEquals("Bearer token-2", r2.getHeader("Authorization")); + String body2 = r2.getBody().readUtf8(); + Assert.assertTrue(body2, body2.contains("\"tokenGroupName\":\"group\"")); + Assert.assertTrue(body2, body2.contains("\"redactionPattern\":\"mask\"")); + } + + @Test + public void getRunQueriesRunByIdScopedToVault() throws Exception { + server.enqueue(new MockResponse().setResponseCode(200).setBody( + "{\"status\":\"SUCCESS\",\"outputType\":\"BASE64\",\"output\":[{\"processedFile\":\"QUJD\"," + + "\"processedFileType\":\"REDACTED_FILE\",\"processedFileExtension\":\"pdf\"}," + + "{\"processedFile\":\"e30=\",\"processedFileType\":\"ENTITIES\",\"processedFileExtension\":\"json\"}]," + + "\"metrics\":{\"pages\":3,\"size\":12.5}}")); + + GetRunResponse response = controller.getRun(GetRunRequest.builder().runId(" run-42 ").build()); + + Assert.assertEquals("run-42", response.getRunId()); + Assert.assertEquals(DetectRunStatus.SUCCESS, response.getStatus()); + Assert.assertEquals(DataSourceType.BASE64, response.getOutputType()); + Assert.assertEquals(2, response.getOutput().size()); + Assert.assertEquals("QUJD", response.getOutput().get(0).getProcessedFile()); + Assert.assertEquals("REDACTED_FILE", response.getOutput().get(0).getProcessedFileType()); + Assert.assertEquals(FileDataFormat.PDF, response.getOutput().get(0).getProcessedFileExtension()); + Assert.assertEquals(FileDataFormat.JSON, response.getOutput().get(1).getProcessedFileExtension()); + Assert.assertEquals(Integer.valueOf(3), response.getMetrics().getPages()); + Assert.assertNull(response.getMessage()); + + RecordedRequest recorded = server.takeRequest(); + Assert.assertEquals("GET", recorded.getMethod()); + Assert.assertEquals("/v2/detect/runs/run-42?vaultId=vault-1", recorded.getPath()); + Assert.assertEquals("Bearer token-1", recorded.getHeader("Authorization")); + Assert.assertEquals("{\"sdk_name_version\":\"test\"}", recorded.getHeader("sky-metadata")); + } + + @Test + public void getRunReportsPendingAndFailedRuns() throws Exception { + server.enqueue(new MockResponse().setResponseCode(200).setBody("{\"status\":\"IN_PROGRESS\"}")); + server.enqueue(new MockResponse().setResponseCode(200).setBody( + "{\"status\":\"FAILED\",\"message\":\"unsupported file\",\"output\":[]}")); + server.enqueue(new MockResponse().setResponseCode(200).setBody("{\"status\":\"SOMETHING_NEW\"}")); + + GetRunResponse pending = controller.getRun(GetRunRequest.builder().runId("r1").build()); + Assert.assertEquals(DetectRunStatus.IN_PROGRESS, pending.getStatus()); + Assert.assertNotNull(pending.getOutput()); + Assert.assertTrue(pending.getOutput().isEmpty()); + Assert.assertNull(pending.getOutputType()); + Assert.assertNull(pending.getMetrics()); + + GetRunResponse failed = controller.getRun(GetRunRequest.builder().runId("r2").build()); + Assert.assertEquals(DetectRunStatus.FAILED, failed.getStatus()); + Assert.assertEquals("unsupported file", failed.getMessage()); + + GetRunResponse unknown = controller.getRun(GetRunRequest.builder().runId("r3").build()); + Assert.assertEquals("unrecognised statuses degrade to UNKNOWN", DetectRunStatus.UNKNOWN, unknown.getStatus()); + } + + @Test + public void getRunValidatesRunIdBeforeTokenOrNetwork() throws Exception { + for (GetRunRequest bad : new GetRunRequest[]{null, GetRunRequest.builder().build(), GetRunRequest.builder().runId(" ").build()}) { + try { + controller.getRun(bad); + Assert.fail("expected SkyflowException for " + bad); + } catch (SkyflowException e) { + Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); + } + } + Assert.assertEquals(0, controller.tokenCalls.get()); + Assert.assertEquals(0, server.getRequestCount()); + } + + @Test + public void getRunMapsNotFound() throws Exception { + server.enqueue(new MockResponse().setResponseCode(404) + .setBody("{\"error\":{\"grpc_code\":5,\"http_code\":404,\"message\":\"run not found\",\"http_status\":\"Not Found\",\"details\":[]}}")); + try { + controller.getRun(GetRunRequest.builder().runId("missing").build()); + Assert.fail("expected SkyflowException"); + } catch (SkyflowException e) { + Assert.assertEquals(404, e.getHttpCode()); + Assert.assertEquals("run not found", e.getMessage()); + } + } + + @Test + public void checkGuardrailsSendsScopedRequestAndMapsResponse() throws Exception { + server.enqueue(new MockResponse().setResponseCode(200).setBody( + "{\"text\":\"tell me about politics\",\"validation\":\"FAILED\",\"toxic\":false,\"deniedTopic\":true}")); + + CheckGuardrailsResponse response = controller.checkGuardrails(CheckGuardrailsRequest.builder() + .text("tell me about politics") + .checkToxicity(true) + .denyTopics(Arrays.asList("politics", "medical advice")) + .build()); + + Assert.assertEquals("tell me about politics", response.getText()); + Assert.assertEquals(GuardrailsValidation.FAILED, response.getValidation()); + Assert.assertEquals(Boolean.FALSE, response.getToxic()); + Assert.assertEquals(Boolean.TRUE, response.getDeniedTopic()); + + RecordedRequest recorded = server.takeRequest(); + Assert.assertEquals("POST", recorded.getMethod()); + Assert.assertEquals("/v2/detect/guardrails", recorded.getPath()); + Assert.assertEquals("Bearer token-1", recorded.getHeader("Authorization")); + Assert.assertEquals("{\"sdk_name_version\":\"test\"}", recorded.getHeader("sky-metadata")); + String body = recorded.getBody().readUtf8(); + Assert.assertTrue(body, body.contains("\"text\":\"tell me about politics\"")); + Assert.assertTrue(body, body.contains("\"vaultId\":\"vault-1\"")); + Assert.assertTrue(body, body.contains("\"checkToxicity\":true")); + Assert.assertTrue(body, body.contains("\"denyTopics\":[\"politics\",\"medical advice\"]")); + } + + @Test + public void checkGuardrailsOmitsUnsetOptionsAndMapsPassed() throws Exception { + server.enqueue(new MockResponse().setResponseCode(200).setBody("{\"text\":\"hello\",\"validation\":\"PASSED\"}")); + CheckGuardrailsResponse response = controller.checkGuardrails(CheckGuardrailsRequest.builder().text("hello").build()); + Assert.assertEquals(GuardrailsValidation.PASSED, response.getValidation()); + Assert.assertNull(response.getToxic()); + Assert.assertNull(response.getDeniedTopic()); + String body = server.takeRequest().getBody().readUtf8(); + Assert.assertFalse(body, body.contains("checkToxicity")); + Assert.assertFalse(body, body.contains("denyTopics")); + } + + @Test + public void checkGuardrailsValidatesBeforeTokenOrNetwork() throws Exception { + CheckGuardrailsRequest[] bad = { + null, + CheckGuardrailsRequest.builder().text(" ").build(), + }; + for (CheckGuardrailsRequest request : bad) { + try { + controller.checkGuardrails(request); + Assert.fail("expected SkyflowException for " + request); + } catch (SkyflowException e) { + Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); + } + } + Assert.assertEquals(0, controller.tokenCalls.get()); + Assert.assertEquals(0, server.getRequestCount()); + } + + @Test + public void deidentifyStringWithStoredConfigurationSendsOnlyTextAndId() throws Exception { + server.enqueue(new MockResponse().setResponseCode(200).setBody("{\"processedText\":\"[NAME_1]\"}")); + controller.deidentifyString(DeidentifyStringRequest.builder().text("John").configurationId("cfg-1").build()); + String body = server.takeRequest().getBody().readUtf8(); + Assert.assertEquals("{\"text\":\"John\",\"configurationId\":\"cfg-1\"}", body); + } + + @Test + public void apiErrorIsMappedToSkyflowExceptionWithServerMessage() throws Exception { + server.enqueue(new MockResponse().setResponseCode(400).setHeader("x-request-id", "req-123") + .setBody("{\"error\":{\"grpc_code\":3,\"http_code\":400,\"message\":\"configuration not found\"," + + "\"http_status\":\"Bad Request\",\"details\":[]}}")); + try { + controller.deidentifyString(DeidentifyStringRequest.builder().text("hello").configurationId("missing").build()); + Assert.fail("expected SkyflowException"); + } catch (SkyflowException e) { + Assert.assertEquals(400, e.getHttpCode()); + Assert.assertEquals("configuration not found", e.getMessage()); + Assert.assertEquals("req-123", e.getRequestId()); + Assert.assertEquals(Integer.valueOf(3), e.getGrpcCode()); + } + } + + @Test + public void unmodelledStatusIsStillMapped() throws Exception { + server.enqueue(new MockResponse().setResponseCode(404) + .setBody("{\"error\":{\"grpc_code\":5,\"http_code\":404,\"message\":\"not found\",\"http_status\":\"Not Found\",\"details\":[]}}")); + try { + controller.reidentifyString(ReidentifyStringRequest.builder().text("[NAME_1]").build()); + Assert.fail("expected SkyflowException"); + } catch (SkyflowException e) { + Assert.assertEquals(404, e.getHttpCode()); + Assert.assertEquals("not found", e.getMessage()); + } + } + + @Test + public void validationFailsBeforeTokenOrNetwork() throws Exception { + try { + controller.deidentifyString(DeidentifyStringRequest.builder().text(" ").configurationId("cfg").build()); + Assert.fail("expected SkyflowException"); + } catch (SkyflowException e) { + Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); + Assert.assertEquals(BaseErrorMessage.InvalidTextInDeidentifyString.getMessage(), e.getMessage()); + } + Assert.assertEquals(0, controller.tokenCalls.get()); + Assert.assertEquals(0, server.getRequestCount()); + } + + @Test + public void tokenFailurePropagatesWithoutNetworkCall() throws Exception { + controller.tokenFailure = new SkyflowException(401, "no credentials"); + try { + controller.reidentifyString(ReidentifyStringRequest.builder().text("[NAME_1]").build()); + Assert.fail("expected SkyflowException"); + } catch (SkyflowException e) { + Assert.assertEquals("no credentials", e.getMessage()); + } + Assert.assertEquals(0, server.getRequestCount()); + } + + @Test + public void clientIsRebuiltWhenUrlOrHttpConfigChanges() throws Exception { + server.enqueue(new MockResponse().setResponseCode(200).setBody("{}")); + controller.reidentifyString(ReidentifyStringRequest.builder().text("[NAME_1]").build()); + Object first = controller.stringsClient(); + Assert.assertSame("same settings must reuse the client", first, controller.stringsClient()); + + controller.setCommonHttpConfig(30, 0, null, null); + Object afterConfig = controller.stringsClient(); + Assert.assertNotSame("changed http config must rebuild", first, afterConfig); + + MockWebServer other = new MockWebServer(); + other.start(); + try { + controller.vaultUrl = other.url("").toString(); + other.enqueue(new MockResponse().setResponseCode(200).setBody("{\"processedText\":\"moved\"}")); + Assert.assertEquals("moved", controller.reidentifyString( + ReidentifyStringRequest.builder().text("[NAME_1]").build()).getProcessedText()); + Assert.assertNotSame("changed url must rebuild", afterConfig, controller.stringsClient()); + Assert.assertEquals(1, other.getRequestCount()); + } finally { + other.shutdown(); + } + } + + @Test + public void setVaultConfigRescopesRequests() throws Exception { + server.enqueue(new MockResponse().setResponseCode(200).setBody("{}")); + server.enqueue(new MockResponse().setResponseCode(200).setBody("{}")); + controller.reidentifyString(ReidentifyStringRequest.builder().text("[NAME_1]").build()); + controller.setVaultConfig(config("vault-2")); + controller.reidentifyString(ReidentifyStringRequest.builder().text("[NAME_1]").build()); + Assert.assertTrue(server.takeRequest().getBody().readUtf8().contains("\"vaultId\":\"vault-1\"")); + Assert.assertTrue(server.takeRequest().getBody().readUtf8().contains("\"vaultId\":\"vault-2\"")); + } +} diff --git a/flowvault/README.md b/flowvault/README.md index c6d78f26..f51dd137 100644 --- a/flowvault/README.md +++ b/flowvault/README.md @@ -49,6 +49,11 @@ The `flowvault` module is a Skyflow Java SDK built for high-throughput vault ope - [Get](#get) - [Update](#update) - [Delete](#delete) +- [Detect](#detect) + - [Deidentify string](#deidentify-string) + - [Reidentify string](#reidentify-string) + - [Get run](#get-run) + - [Check guardrails](#check-guardrails) - [Custom Request Headers](#custom-request-headers) - [Error Handling](#error-handling) - [Two layers of errors](#two-layers-of-errors) @@ -1624,6 +1629,212 @@ for (DeleteResponseRecord record : deleteResponse.getRecords()) { } ``` +# Detect + +Skyflow Detect finds and de-identifies sensitive data in free text, then re-identifies it on demand. The flowvault SDK exposes the Detect V2 API through `DetectController`, returned by `skyflowClient.detect()` and `skyflowClient.detect(vaultId)`. Every request is scoped to that vault, and the controller honours the same credentials, [timeouts and retries](#timeouts-and-retries) as `vault()`. + +Detect configurations are created in Studio or through the management API. The SDK does not create, update or delete them. + +| Method | Request | Response | Description | +|--------|---------|----------|-------------| +| `deidentifyString(DeidentifyStringRequest)` | `com.skyflow.detect.DeidentifyStringRequest` | `com.skyflow.detect.DeidentifyStringResponse` | Detect and replace sensitive data in a string | +| `deidentifyFile(DeidentifyFileRequest)` | `com.skyflow.detect.DeidentifyFileRequest` | `com.skyflow.detect.DeidentifyFileResponse` | Submit a file for de-identification, optionally polling the run to completion | +| `reidentifyString(ReidentifyStringRequest)` | `com.skyflow.detect.ReidentifyStringRequest` | `com.skyflow.detect.ReidentifyStringResponse` | Replace tokens in a string with their original or redacted values | +| `reidentifyFile(ReidentifyFileRequest)` | `com.skyflow.detect.ReidentifyFileRequest` | `com.skyflow.detect.ReidentifyFileResponse` | Replace tokens in a file with their original or redacted values | +| `getRun(GetRunRequest)` | `com.skyflow.detect.GetRunRequest` | `com.skyflow.detect.GetRunResponse` | Retrieve the status and outputs of a Detect file run | +| `checkGuardrails(CheckGuardrailsRequest)` | `com.skyflow.detect.CheckGuardrailsRequest` | `com.skyflow.detect.CheckGuardrailsResponse` | Screen text for toxicity and denied topics | + +> Detect V2 is in beta. + +## Deidentify string + +`DeidentifyStringRequest` takes the text plus, optionally, `configurationId` (a stored Detect configuration) and/or `configuration` (an inline `DetectConfiguration`). Both are optional on the API and are sent exactly as supplied. The SDK fills the vault id from the controller. + +```java +import com.skyflow.detect.*; + +// Stored configuration +DeidentifyStringResponse response = skyflowClient.detect().deidentifyString( + DeidentifyStringRequest.builder() + .text("My name is John Doe, and my email is johndoe@acme.com.") + .configurationId("") + .build()); + +System.out.println(response.getProcessedText()); +// My name is [NAME_1], and my email is [EMAIL_ADDRESS_1]. + +// Inline configuration +DetectConfiguration configuration = DetectConfiguration.builder() + .detect(Detect.builder() + .entities(Arrays.asList( + Entity.builder() + .entityType(EntityType.NAME) + .deidentificationType(DeidentificationType.VAULT_TOKEN) + .destination("") // token group that stores the value + .build(), + Entity.builder() + .entityType(EntityType.ALL) + .deidentificationType(DeidentificationType.ENTITY_UNIQUE_COUNTER) + .build())) + .skip(Collections.singletonList("Skyflow")) + .returnEntities(ReturnEntitiesType.EXCLUDE_SENSITIVE_DATA) + .build()) + .build(); + +response = skyflowClient.detect().deidentifyString( + DeidentifyStringRequest.builder() + .text("My name is John Doe, and my email is johndoe@acme.com.") + .configuration(configuration) + .build()); + +for (DetectedEntity entity : response.getEntities()) { + System.out.println(entity.getEntityType() + " " + entity.getToken() + + " [" + entity.getLocation().getStartIndex() + "," + entity.getLocation().getEndIndex() + "]"); +} +System.out.println(response.getMetrics().getWordCount()); +``` + +`DeidentifyStringResponse` carries `getProcessedText()`, `getEntities()` (a `List` with token, entity type, location and confidence scores; never `null`) and `getMetrics()`. + +## Deidentify file + +File de-identification is asynchronous on the API: the submit call returns a run id and the result is retrieved from the run. `deidentifyFile` exposes both modes. + +- **Submit only** (default): one call, returns `getRunId()`. `getStatus()` is `null` and nothing is written to disk. Retrieve the result later with [`getRun`](#get-run). +- **Submit and poll**: set `pollOptions`. The SDK polls the run with exponential backoff (1, 2, 4, 8, 16, then 32 seconds between lookups) until it is `SUCCESS`, `FAILED` or `UNKNOWN`, or until `waitTime` (default 60 s, max 300) or `maxAttempts` (default 23, max 23) is used up. If the budget runs out the response carries the run id with `IN_PROGRESS` or `QUEUED` so you can resume with `getRun`. + +The file is supplied in one of three ways: `dataSource` plus `value` (`BASE64` content, a `SKYFLOW_ID`, or a `PRESIGNED_URL`), or `file(File)` / `filePath(String)`, which read and encode a local file and infer `dataFormat` from its extension. Supply exactly one of `value`, `file` or `filePath`. Either `configurationId` or an inline `configuration` is passed through as given. + +```java +// Submit only +DeidentifyFileResponse submitted = skyflowClient.detect().deidentifyFile( + DeidentifyFileRequest.builder() + .filePath("/data/intake/patient-notes.pdf") // BASE64 + pdf inferred + .configurationId("") + .build()); +String runId = submitted.getRunId(); + +// Submit and poll +DeidentifyFileResponse response = skyflowClient.detect().deidentifyFile( + DeidentifyFileRequest.builder() + .filePath("/data/intake/patient-notes.pdf") + .configurationId("") + .pollOptions(PollOptions.builder().waitTime(120).build()) + .outputDirectory("/data/processed") // optional + .build()); + +switch (response.getStatus()) { + case SUCCESS: + // For file-based requests the outputs are already on disk: + // /data/processed/processed-patient-notes.pdf the redacted file + // /data/processed/processed-entities-patient-notes.json the detected entities + // response.getOutput() and getMetrics() carry the same data. + break; + case FAILED: + System.err.println(response.getMessage()); + break; + default: // IN_PROGRESS or QUEUED: budget ran out, resume with getRun(runId) + break; +} +``` + +Outputs are written only when polling reached `SUCCESS`, the request was built from a local file, and the run delivered them as base64. A request built from a `SKYFLOW_ID` or `PRESIGNED_URL` has no local name to derive from, and presigned-URL outputs are not fetched, so in those cases read `getOutput()` and write the files yourself. Files are named `processed-.`, with the entities output as `processed-entities-.json` (and `processed-object-entities-.json` for object entities); `outputDirectory` is created if missing and defaults to the working directory. Polling blocks the calling thread. + +## Reidentify string + +`ReidentifyStringRequest` takes the tokenized text and an optional list of `RedactionLevel` rules. For flowvault vaults each rule targets a `tokenGroupName` and applies a named `redactionPattern`. Without rules every token is replaced by its original value. + +```java +ReidentifyStringResponse response = skyflowClient.detect().reidentifyString( + ReidentifyStringRequest.builder() + .text("My name is [NAME_1].") + .redactionLevel(Collections.singletonList( + RedactionLevel.builder() + .tokenGroupName("") + .redactionPattern("") + .build())) + .build()); + +System.out.println(response.getProcessedText()); +// My name is J**n D*e. +``` + +A `RedactionLevel` is expected to name one source (`tokenGroupName` or `entityName`) and one replacement (`redactionPattern` or `redactionType`). Every field is optional on the API, so the SDK passes the list through as given and the server enforces the combination rules, including which fields apply to flowvault vaults. + +## Reidentify file + +`reidentifyFile` is synchronous: the re-identified file comes back in the response, not through a run id. `ReidentifyFileRequest` takes a `dataSource` (`BASE64`, `SKYFLOW_ID` or `PRESIGNED_URL`), the matching `value`, an optional `dataFormat` and the same optional `RedactionLevel` rules as `reidentifyString`. Re-identification supports text-based formats such as TXT, CSV, JSON, JSONL, XML, DOCX, XLSX and PPTX. + +```java +byte[] content = Files.readAllBytes(Paths.get("tokenized.txt")); + +ReidentifyFileResponse response = skyflowClient.detect().reidentifyFile( + ReidentifyFileRequest.builder() + .dataSource(DataSourceType.BASE64) + .value(Base64.getEncoder().encodeToString(content)) + .dataFormat(FileDataFormat.TXT) + .redactionLevel(Collections.singletonList( + RedactionLevel.builder() + .tokenGroupName("") + .redactionPattern("") + .build())) + .build()); + +if (response.getStatus() == DetectRunStatus.SUCCESS) { + for (FileOutput output : response.getOutput()) { + // output.getProcessedFile() is base64 or a presigned URL, per response.getOutputType() + byte[] processed = Base64.getDecoder().decode(output.getProcessedFile()); + } +} +``` + +`ReidentifyFileResponse` carries `getStatus()`, `getOutputType()`, `getOutput()` (never `null`) and `getMetrics()`. This method is specific to the flowvault SDK. + +## Get run + +File de-identification is asynchronous: the submit call returns a run id, and `getRun` retrieves the run's status and, once it has succeeded, its outputs. `GetRunResponse.getOutput()` is empty and `getOutputType()` and `getMetrics()` are `null` until the run completes. + +```java +GetRunResponse run = skyflowClient.detect().getRun( + GetRunRequest.builder().runId("").build()); + +switch (run.getStatus()) { + case SUCCESS: + for (FileOutput output : run.getOutput()) { + // output.getProcessedFile() is base64 or a presigned URL, per run.getOutputType() + // output.getProcessedFileType() is e.g. REDACTED_FILE or ENTITIES + // output.getProcessedFileExtension() is a FileDataFormat such as PDF + } + break; + case FAILED: + System.err.println(run.getMessage()); + break; + default: // QUEUED, IN_PROGRESS, UNKNOWN + // poll again later +} +``` + +## Check guardrails + +`checkGuardrails` screens text for toxicity and for a list of denied topics, which makes it a natural pre-LLM check. Each check runs only when requested: `getToxic()` is `null` unless `checkToxicity(true)` was set, and `getDeniedTopic()` is `null` unless `denyTopics` was supplied. `getValidation()` is `FAILED` if any requested check triggered. + +```java +CheckGuardrailsResponse response = skyflowClient.detect().checkGuardrails( + CheckGuardrailsRequest.builder() + .text(userPrompt) + .checkToxicity(true) + .denyTopics(Arrays.asList("politics", "medical advice")) + .build()); + +if (response.getValidation() == GuardrailsValidation.FAILED) { + throw new IllegalArgumentException("blocked: toxic=" + response.getToxic() + " deniedTopic=" + response.getDeniedTopic()); +} +``` + +All Detect methods throw `SkyflowException` as described in [Error Handling](#error-handling). Client-side validation fails before any network call. + +See the samples [DeidentifyStringSample.java](samples/src/main/java/com/example/detect/DeidentifyStringSample.java), [ReidentifyStringSample.java](samples/src/main/java/com/example/detect/ReidentifyStringSample.java) [GetRunSample.java](samples/src/main/java/com/example/detect/GetRunSample.java) and [CheckGuardrailsSample.java](samples/src/main/java/com/example/detect/CheckGuardrailsSample.java). + # Custom Request Headers To include custom HTTP headers on an outgoing request — bulk or unary — pass a `RequestInterceptor` via that operation's options object. The headers available are defined by the `CustomHeaderKey` enum: diff --git a/flowvault/api-report/skyflow-flowvault-java.baseline.jar b/flowvault/api-report/skyflow-flowvault-java.baseline.jar index 1defab78..733abb00 100644 Binary files a/flowvault/api-report/skyflow-flowvault-java.baseline.jar and b/flowvault/api-report/skyflow-flowvault-java.baseline.jar differ diff --git a/flowvault/pom.xml b/flowvault/pom.xml index 6ea99c9a..ea28c99b 100644 --- a/flowvault/pom.xml +++ b/flowvault/pom.xml @@ -12,7 +12,7 @@ skyflow-flowvault-java - 1.2.0 + 1.2.0-dev.5aa7241b jar ${project.groupId}:${project.artifactId} Skyflow FlowVault SDK for the Java programming language @@ -48,6 +48,12 @@ 1.0.0 compile + + com.squareup.okhttp3 + mockwebserver + ${okhttp-version} + test + @@ -76,25 +82,18 @@ - - - - - org.apache.maven.plugins - maven-shade-plugin - 3.6.0 - + shade-for-japicmp package diff --git a/flowvault/samples/src/main/java/com/example/detect/CheckGuardrailsSample.java b/flowvault/samples/src/main/java/com/example/detect/CheckGuardrailsSample.java new file mode 100644 index 00000000..a48efaf2 --- /dev/null +++ b/flowvault/samples/src/main/java/com/example/detect/CheckGuardrailsSample.java @@ -0,0 +1,53 @@ +package com.example.detect; + +import com.skyflow.Skyflow; +import com.skyflow.config.Credentials; +import com.skyflow.config.VaultConfig; +import com.skyflow.detect.CheckGuardrailsRequest; +import com.skyflow.detect.CheckGuardrailsResponse; +import com.skyflow.detect.GuardrailsValidation; +import com.skyflow.enums.Env; +import com.skyflow.enums.LogLevel; +import com.skyflow.errors.SkyflowException; + +import java.util.Arrays; + +/** + * Screens a prompt for toxicity and denied topics before it is sent to an LLM. + */ +public class CheckGuardrailsSample { + public static void main(String[] args) throws SkyflowException { + Credentials credentials = new Credentials(); + credentials.setPath(""); // Replace with the path to the credentials file + + VaultConfig vaultConfig = new VaultConfig(); + vaultConfig.setVaultId(""); // Replace with the ID of the vault + vaultConfig.setClusterId(""); // Replace with the cluster ID of the vault + vaultConfig.setEnv(Env.PROD); // Set the environment (e.g., DEV, STAGE, PROD) + + Skyflow skyflowClient = Skyflow.builder() + .setLogLevel(LogLevel.ERROR) + .addVaultConfig(vaultConfig) + .addSkyflowCredentials(credentials) + .build(); + + String userPrompt = "Which party should I vote for?"; + try { + CheckGuardrailsResponse response = skyflowClient.detect().checkGuardrails( + CheckGuardrailsRequest.builder() + .text(userPrompt) + .checkToxicity(true) + .denyTopics(Arrays.asList("politics", "medical advice")) + .build()); + + if (response.getValidation() == GuardrailsValidation.FAILED) { + System.out.println("Blocked: toxic=" + response.getToxic() + " deniedTopic=" + response.getDeniedTopic()); + } else { + System.out.println("Prompt passed guardrails."); + } + } catch (SkyflowException e) { + System.err.println("Error during checkGuardrails:"); + e.printStackTrace(); + } + } +} diff --git a/flowvault/samples/src/main/java/com/example/detect/DeidentifyFileSample.java b/flowvault/samples/src/main/java/com/example/detect/DeidentifyFileSample.java new file mode 100644 index 00000000..c373f181 --- /dev/null +++ b/flowvault/samples/src/main/java/com/example/detect/DeidentifyFileSample.java @@ -0,0 +1,66 @@ +package com.example.detect; + +import com.skyflow.Skyflow; +import com.skyflow.config.Credentials; +import com.skyflow.config.VaultConfig; +import com.skyflow.detect.DeidentifyFileRequest; +import com.skyflow.detect.DeidentifyFileResponse; +import com.skyflow.detect.DetectRunStatus; +import com.skyflow.detect.PollOptions; +import com.skyflow.enums.Env; +import com.skyflow.enums.LogLevel; +import com.skyflow.errors.SkyflowException; + +/** + * De-identifies a local file with the Detect V2 API. The first call submits the run and returns its + * id; the second submits and polls, and writes the outputs next to the input on success. + */ +public class DeidentifyFileSample { + public static void main(String[] args) throws SkyflowException { + Credentials credentials = new Credentials(); + credentials.setPath(""); // Replace with the path to the credentials file + + VaultConfig vaultConfig = new VaultConfig(); + vaultConfig.setVaultId(""); // Replace with the ID of the vault + vaultConfig.setClusterId(""); // Replace with the cluster ID of the vault + vaultConfig.setEnv(Env.PROD); // Set the environment (e.g., DEV, STAGE, PROD) + + Skyflow skyflowClient = Skyflow.builder() + .setLogLevel(LogLevel.ERROR) + .addVaultConfig(vaultConfig) + .addSkyflowCredentials(credentials) + .build(); + + try { + // Submit only: the API's own behaviour. Keep the run id and retrieve the result with getRun. + DeidentifyFileResponse submitted = skyflowClient.detect().deidentifyFile( + DeidentifyFileRequest.builder() + .filePath("") // BASE64 and the format are derived from the file + .configurationId("") // Replace with a Detect configuration id + .build()); + System.out.println("Submitted run " + submitted.getRunId()); + + // Submit and poll: waits up to 90 seconds with exponential backoff, then writes + // processed-. and processed-.json into the output directory. + DeidentifyFileResponse polled = skyflowClient.detect().deidentifyFile( + DeidentifyFileRequest.builder() + .filePath("") + .configurationId("") + .pollOptions(PollOptions.builder().waitTime(90).build()) + .outputDirectory("") // optional; defaults to the working directory + .build()); + + System.out.println("Run " + polled.getRunId() + " status: " + polled.getStatus()); + if (polled.getStatus() == DetectRunStatus.SUCCESS) { + System.out.println("Outputs: " + polled.getOutput().size() + ", metrics: " + polled.getMetrics()); + } else if (polled.getStatus() == DetectRunStatus.FAILED) { + System.err.println("Run failed: " + polled.getMessage()); + } else { + System.out.println("Still running; poll later with getRun(" + polled.getRunId() + ")"); + } + } catch (SkyflowException e) { + System.err.println("Error during deidentifyFile:"); + e.printStackTrace(); + } + } +} diff --git a/flowvault/samples/src/main/java/com/example/detect/DeidentifyStringSample.java b/flowvault/samples/src/main/java/com/example/detect/DeidentifyStringSample.java new file mode 100644 index 00000000..b38ba42e --- /dev/null +++ b/flowvault/samples/src/main/java/com/example/detect/DeidentifyStringSample.java @@ -0,0 +1,94 @@ +package com.example.detect; + +import com.skyflow.Skyflow; +import com.skyflow.config.Credentials; +import com.skyflow.config.VaultConfig; +import com.skyflow.detect.DeidentificationType; +import com.skyflow.detect.DeidentifyStringRequest; +import com.skyflow.detect.DeidentifyStringResponse; +import com.skyflow.detect.Detect; +import com.skyflow.detect.DetectConfiguration; +import com.skyflow.detect.DetectedEntity; +import com.skyflow.detect.Entity; +import com.skyflow.detect.EntityType; +import com.skyflow.detect.ReturnEntitiesType; +import com.skyflow.enums.Env; +import com.skyflow.enums.LogLevel; +import com.skyflow.errors.SkyflowException; + +import java.util.Arrays; +import java.util.Collections; + +/** + * De-identifies sensitive data in a string with the Detect V2 API, first with a stored + * configuration id and then with an inline configuration. + */ +public class DeidentifyStringSample { + public static void main(String[] args) throws SkyflowException { + Credentials credentials = new Credentials(); + credentials.setPath(""); // Replace with the path to the credentials file + + VaultConfig vaultConfig = new VaultConfig(); + vaultConfig.setVaultId(""); // Replace with the ID of the vault + vaultConfig.setClusterId(""); // Replace with the cluster ID of the vault + vaultConfig.setEnv(Env.PROD); // Set the environment (e.g., DEV, STAGE, PROD) + + Skyflow skyflowClient = Skyflow.builder() + .setLogLevel(LogLevel.ERROR) + .addVaultConfig(vaultConfig) + .addSkyflowCredentials(credentials) + .build(); + + String text = "My name is John Doe, and my email is johndoe@acme.com."; + + // 1. Stored configuration, created in Studio or through the management API. + try { + DeidentifyStringResponse response = skyflowClient.detect().deidentifyString( + DeidentifyStringRequest.builder() + .text(text) + .configurationId("") // Replace with a Detect configuration id + .build()); + System.out.println("Processed text: " + response.getProcessedText()); + } catch (SkyflowException e) { + System.err.println("Error during deidentifyString (stored configuration):"); + e.printStackTrace(); + } + + // 2. Inline configuration, nothing to create beforehand. + DetectConfiguration configuration = DetectConfiguration.builder() + .detect(Detect.builder() + .entities(Arrays.asList( + Entity.builder() + .entityType(EntityType.NAME) + .deidentificationType(DeidentificationType.VAULT_TOKEN) + .destination("") // flowvault: token group that stores the value + .build(), + Entity.builder() + .entityType(EntityType.ALL) + .deidentificationType(DeidentificationType.ENTITY_UNIQUE_COUNTER) + .build())) + .skip(Collections.singletonList("Skyflow")) + .returnEntities(ReturnEntitiesType.EXCLUDE_SENSITIVE_DATA) + .build()) + .build(); + + try { + DeidentifyStringResponse response = skyflowClient.detect().deidentifyString( + DeidentifyStringRequest.builder() + .text(text) + .configuration(configuration) + .build()); + System.out.println("Processed text: " + response.getProcessedText()); + for (DetectedEntity entity : response.getEntities()) { + System.out.printf("%-16s %-18s [%d,%d] %s%n", + entity.getEntityType(), entity.getToken(), + entity.getLocation().getStartIndex(), entity.getLocation().getEndIndex(), + entity.getEntityScores()); + } + System.out.println("Metrics: " + response.getMetrics()); + } catch (SkyflowException e) { + System.err.println("Error during deidentifyString (inline configuration):"); + e.printStackTrace(); + } + } +} diff --git a/flowvault/samples/src/main/java/com/example/detect/GetRunSample.java b/flowvault/samples/src/main/java/com/example/detect/GetRunSample.java new file mode 100644 index 00000000..cc3e7667 --- /dev/null +++ b/flowvault/samples/src/main/java/com/example/detect/GetRunSample.java @@ -0,0 +1,62 @@ +package com.example.detect; + +import com.skyflow.Skyflow; +import com.skyflow.config.Credentials; +import com.skyflow.config.VaultConfig; +import com.skyflow.detect.DetectRunStatus; +import com.skyflow.detect.FileOutput; +import com.skyflow.detect.GetRunRequest; +import com.skyflow.detect.GetRunResponse; +import com.skyflow.enums.Env; +import com.skyflow.enums.LogLevel; +import com.skyflow.errors.SkyflowException; + +import java.io.File; +import java.nio.file.Files; +import java.util.Base64; + +/** + * Retrieves a Detect file run by id and, once it has succeeded, writes the base64 outputs to disk. + */ +public class GetRunSample { + public static void main(String[] args) throws Exception { + Credentials credentials = new Credentials(); + credentials.setPath(""); // Replace with the path to the credentials file + + VaultConfig vaultConfig = new VaultConfig(); + vaultConfig.setVaultId(""); // Replace with the ID of the vault + vaultConfig.setClusterId(""); // Replace with the cluster ID of the vault + vaultConfig.setEnv(Env.PROD); // Set the environment (e.g., DEV, STAGE, PROD) + + Skyflow skyflowClient = Skyflow.builder() + .setLogLevel(LogLevel.ERROR) + .addVaultConfig(vaultConfig) + .addSkyflowCredentials(credentials) + .build(); + + try { + GetRunResponse run = skyflowClient.detect().getRun( + GetRunRequest.builder() + .runId("") // Replace with the run id returned by deidentifyFile + .build()); + + System.out.println("Run " + run.getRunId() + " status: " + run.getStatus()); + if (run.getStatus() == DetectRunStatus.SUCCESS) { + for (FileOutput output : run.getOutput()) { + String name = "processed-" + run.getRunId() + "-" + output.getProcessedFileType() + + "." + output.getProcessedFileExtension().name().toLowerCase(); + Files.write(new File(name).toPath(), Base64.getDecoder().decode(output.getProcessedFile())); + System.out.println("Wrote " + name); + } + System.out.println("Metrics: " + run.getMetrics()); + } else if (run.getStatus() == DetectRunStatus.FAILED) { + System.err.println("Run failed: " + run.getMessage()); + } else { + System.out.println("Run still pending, poll again later."); + } + } catch (SkyflowException e) { + System.err.println("Error during getRun:"); + e.printStackTrace(); + } + } +} diff --git a/flowvault/samples/src/main/java/com/example/detect/ReidentifyFileSample.java b/flowvault/samples/src/main/java/com/example/detect/ReidentifyFileSample.java new file mode 100644 index 00000000..587d58eb --- /dev/null +++ b/flowvault/samples/src/main/java/com/example/detect/ReidentifyFileSample.java @@ -0,0 +1,74 @@ +package com.example.detect; + +import com.skyflow.Skyflow; +import com.skyflow.config.Credentials; +import com.skyflow.config.VaultConfig; +import com.skyflow.detect.DataSourceType; +import com.skyflow.detect.DetectRunStatus; +import com.skyflow.detect.FileDataFormat; +import com.skyflow.detect.FileOutput; +import com.skyflow.detect.RedactionLevel; +import com.skyflow.detect.ReidentifyFileRequest; +import com.skyflow.detect.ReidentifyFileResponse; +import com.skyflow.enums.Env; +import com.skyflow.enums.LogLevel; +import com.skyflow.errors.SkyflowException; + +import java.io.File; +import java.nio.file.Files; +import java.util.Base64; +import java.util.Collections; + +/** + * Re-identifies the tokens in a text file with the Detect V2 API. The file is sent as base64 and + * the re-identified file is returned synchronously and written next to the input. + */ +public class ReidentifyFileSample { + public static void main(String[] args) throws Exception { + Credentials credentials = new Credentials(); + credentials.setPath(""); // Replace with the path to the credentials file + + VaultConfig vaultConfig = new VaultConfig(); + vaultConfig.setVaultId(""); // Replace with the ID of the vault + vaultConfig.setClusterId(""); // Replace with the cluster ID of the vault + vaultConfig.setEnv(Env.PROD); // Set the environment (e.g., DEV, STAGE, PROD) + + Skyflow skyflowClient = Skyflow.builder() + .setLogLevel(LogLevel.ERROR) + .addVaultConfig(vaultConfig) + .addSkyflowCredentials(credentials) + .build(); + + File input = new File(""); // Replace with a file produced by deidentification + String base64 = Base64.getEncoder().encodeToString(Files.readAllBytes(input.toPath())); + + try { + ReidentifyFileResponse response = skyflowClient.detect().reidentifyFile( + ReidentifyFileRequest.builder() + .dataSource(DataSourceType.BASE64) + .value(base64) + .dataFormat(FileDataFormat.TXT) + // Optional: render one token group with a named redaction pattern instead of its plain value. + .redactionLevel(Collections.singletonList( + RedactionLevel.builder() + .tokenGroupName("") // Replace with a token group name + .redactionPattern("") // Replace with a redaction pattern name + .build())) + .build()); + + System.out.println("Status: " + response.getStatus()); + if (response.getStatus() == DetectRunStatus.SUCCESS) { + for (FileOutput output : response.getOutput()) { + String name = "reidentified-" + input.getName(); + Files.write(new File(input.getParentFile(), name).toPath(), + Base64.getDecoder().decode(output.getProcessedFile())); + System.out.println("Wrote " + name + " (" + output.getProcessedFileType() + ")"); + } + System.out.println("Metrics: " + response.getMetrics()); + } + } catch (SkyflowException e) { + System.err.println("Error during reidentifyFile:"); + e.printStackTrace(); + } + } +} diff --git a/flowvault/samples/src/main/java/com/example/detect/ReidentifyStringSample.java b/flowvault/samples/src/main/java/com/example/detect/ReidentifyStringSample.java new file mode 100644 index 00000000..508bcc6c --- /dev/null +++ b/flowvault/samples/src/main/java/com/example/detect/ReidentifyStringSample.java @@ -0,0 +1,59 @@ +package com.example.detect; + +import com.skyflow.Skyflow; +import com.skyflow.config.Credentials; +import com.skyflow.config.VaultConfig; +import com.skyflow.detect.RedactionLevel; +import com.skyflow.detect.ReidentifyStringRequest; +import com.skyflow.detect.ReidentifyStringResponse; +import com.skyflow.enums.Env; +import com.skyflow.enums.LogLevel; +import com.skyflow.errors.SkyflowException; + +import java.util.Collections; + +/** + * Re-identifies tokens in a string with the Detect V2 API. For flowvault vaults, redaction + * levels target a token group and apply a named redaction pattern. + */ +public class ReidentifyStringSample { + public static void main(String[] args) throws SkyflowException { + Credentials credentials = new Credentials(); + credentials.setPath(""); // Replace with the path to the credentials file + + VaultConfig vaultConfig = new VaultConfig(); + vaultConfig.setVaultId(""); // Replace with the ID of the vault + vaultConfig.setClusterId(""); // Replace with the cluster ID of the vault + vaultConfig.setEnv(Env.PROD); // Set the environment (e.g., DEV, STAGE, PROD) + + Skyflow skyflowClient = Skyflow.builder() + .setLogLevel(LogLevel.ERROR) + .addVaultConfig(vaultConfig) + .addSkyflowCredentials(credentials) + .build(); + + try { + // Plain re-identification: every token is replaced by its original value. + ReidentifyStringResponse response = skyflowClient.detect().reidentifyString( + ReidentifyStringRequest.builder() + .text("My name is [NAME_1] and my email is [EMAIL_ADDRESS_1].") + .build()); + System.out.println("Processed text: " + response.getProcessedText()); + + // Targeted rendering: tokens from one token group are rendered with a named pattern. + ReidentifyStringResponse masked = skyflowClient.detect().reidentifyString( + ReidentifyStringRequest.builder() + .text("My name is [NAME_1].") + .redactionLevel(Collections.singletonList( + RedactionLevel.builder() + .tokenGroupName("") // Replace with a token group name + .redactionPattern("") // Replace with a redaction pattern name + .build())) + .build()); + System.out.println("Masked text: " + masked.getProcessedText()); + } catch (SkyflowException e) { + System.err.println("Error during reidentifyString:"); + e.printStackTrace(); + } + } +} diff --git a/flowvault/src/main/java/com/skyflow/Skyflow.java b/flowvault/src/main/java/com/skyflow/Skyflow.java index c2647824..b1955b63 100644 --- a/flowvault/src/main/java/com/skyflow/Skyflow.java +++ b/flowvault/src/main/java/com/skyflow/Skyflow.java @@ -3,7 +3,7 @@ import com.skyflow.config.Credentials; import com.skyflow.config.VaultConfig; import com.skyflow.enums.LogLevel; -import com.skyflow.errors.ErrorMessage; +import com.skyflow.errors.BaseErrorMessage; import com.skyflow.errors.SkyflowException; import com.skyflow.logs.ErrorLogs; import com.skyflow.logs.InfoLogs; @@ -12,6 +12,7 @@ import com.skyflow.utils.Utils; import com.skyflow.utils.logger.LogUtil; import com.skyflow.utils.validations.Validations; +import com.skyflow.vault.controller.DetectController; import com.skyflow.vault.controller.VaultController; import java.util.LinkedHashMap; @@ -55,16 +56,38 @@ public Skyflow updateVaultConfig(VaultConfig vaultConfig) throws SkyflowExceptio } public VaultController vault() throws SkyflowException { - return resolveOrThrow(this.builder.vaultClientsMap, null, ErrorLogs.VAULT_CONFIG_DOES_NOT_EXIST, ErrorMessage.VaultIdNotInConfigList); + return resolveOrThrow(this.builder.vaultClientsMap, null, ErrorLogs.VAULT_CONFIG_DOES_NOT_EXIST, BaseErrorMessage.VaultIdNotInConfigList); } public VaultController vault(String vaultId) throws SkyflowException { - return resolveOrThrow(this.builder.vaultClientsMap, vaultId, ErrorLogs.VAULT_CONFIG_DOES_NOT_EXIST, ErrorMessage.VaultIdNotInConfigList); + return resolveOrThrow(this.builder.vaultClientsMap, vaultId, ErrorLogs.VAULT_CONFIG_DOES_NOT_EXIST, BaseErrorMessage.VaultIdNotInConfigList); + } + + /** + * Detect operations for the single configured vault. + * + * @return the detect controller + * @throws SkyflowException if no vault is configured + */ + public DetectController detect() throws SkyflowException { + return resolveOrThrow(this.builder.detectClientsMap, null, ErrorLogs.VAULT_CONFIG_DOES_NOT_EXIST, BaseErrorMessage.VaultIdNotInConfigList); + } + + /** + * Detect operations for a specific configured vault. + * + * @param vaultId id of a configured vault + * @return the detect controller + * @throws SkyflowException if the vault is not configured + */ + public DetectController detect(String vaultId) throws SkyflowException { + return resolveOrThrow(this.builder.detectClientsMap, vaultId, ErrorLogs.VAULT_CONFIG_DOES_NOT_EXIST, BaseErrorMessage.VaultIdNotInConfigList); } public static final class SkyflowClientBuilder extends BaseSkyflowClientBuilder { private final LinkedHashMap vaultClientsMap = new LinkedHashMap<>(); + private final LinkedHashMap detectClientsMap = new LinkedHashMap<>(); // Client-wide HTTP config. Resolution per vault, most specific first: // VaultConfig value -> the value set here -> SDK default (60s call timeout, 0 retries). // null here means "not set", so the SDK default applies to vaults that don't override it. @@ -89,6 +112,9 @@ protected void onVaultConfigAdded(VaultConfig vaultConfig) throws SkyflowExcepti this.writeTimeout, this.maxRetries, this.initialRetryDelayMillis, this.maxRetryDelayMillis); this.vaultClientsMap.put(vaultConfig.getVaultId(), controller); + DetectController detectController = new DetectController(vaultConfig, this.skyflowCredentials); + detectController.setCommonHttpConfig(this.timeout, this.maxRetries, this.initialRetryDelayMillis, this.maxRetryDelayMillis); + this.detectClientsMap.put(vaultConfig.getVaultId(), detectController); LogUtil.printInfoLog(Utils.parameterizedString(InfoLogs.VAULT_CONTROLLER_INITIALIZED.getLog(), vaultConfig.getVaultId())); } @@ -106,11 +132,20 @@ protected void onVaultConfigUpdated(VaultConfig updatedConfig) throws SkyflowExc updated.setCommonHttpConfig(this.timeout, this.connectTimeout, this.readTimeout, this.writeTimeout, this.maxRetries, this.initialRetryDelayMillis, this.maxRetryDelayMillis); + DetectController updatedDetect = this.detectClientsMap.get(updatedConfig.getVaultId()); + if (updatedDetect == null) { + updatedDetect = new DetectController(updatedConfig, this.skyflowCredentials); + this.detectClientsMap.put(updatedConfig.getVaultId(), updatedDetect); + } else { + updatedDetect.setVaultConfig(updatedConfig); + } + updatedDetect.setCommonHttpConfig(this.timeout, this.maxRetries, this.initialRetryDelayMillis, this.maxRetryDelayMillis); } @Override protected void onVaultConfigRemoved(String vaultId) throws SkyflowException { this.vaultClientsMap.remove(vaultId); + this.detectClientsMap.remove(vaultId); } @Override @@ -123,6 +158,9 @@ protected void onCredentialsUpdated(Credentials credentials) throws SkyflowExcep for (VaultController vault : this.vaultClientsMap.values()) { vault.setCommonCredentials(credentials); } + for (DetectController detect : this.detectClientsMap.values()) { + detect.setCommonCredentials(credentials); + } } @Override @@ -179,6 +217,10 @@ private void carryVaultOverrides(VaultConfig incoming) throws SkyflowException { if (controller != null) { controller.refreshVaultUrl(); } + DetectController detectController = this.detectClientsMap.get(incoming.getVaultId()); + if (detectController != null) { + detectController.refreshVaultUrl(); + } } } @@ -294,6 +336,10 @@ private void propagateHttpConfig() { this.writeTimeout, this.maxRetries, this.initialRetryDelayMillis, this.maxRetryDelayMillis); } + for (DetectController detect : this.detectClientsMap.values()) { + detect.setCommonHttpConfig(this.timeout, this.maxRetries, this.initialRetryDelayMillis, + this.maxRetryDelayMillis); + } } public Skyflow build() { diff --git a/flowvault/src/main/java/com/skyflow/detect/ReidentifyFileRequest.java b/flowvault/src/main/java/com/skyflow/detect/ReidentifyFileRequest.java new file mode 100644 index 00000000..9aa7135e --- /dev/null +++ b/flowvault/src/main/java/com/skyflow/detect/ReidentifyFileRequest.java @@ -0,0 +1,118 @@ +package com.skyflow.detect; + +import java.util.List; + +/** + * Request for {@code reidentifyFile}. Supply the file through exactly one {@link DataSourceType}: + * the base64-encoded content, the skyflow id of a vault record that holds the file, or an S3 + * presigned URL. The vault id is filled in by the controller. + */ +public final class ReidentifyFileRequest { + private final DataSourceType dataSource; + private final String value; + private final FileDataFormat dataFormat; + private final List redactionLevel; + + private ReidentifyFileRequest(ReidentifyFileRequestBuilder builder) { + this.dataSource = builder.dataSource; + this.value = builder.value; + this.dataFormat = builder.dataFormat; + this.redactionLevel = builder.redactionLevel; + } + + /** + * @return a new builder + */ + public static ReidentifyFileRequestBuilder builder() { + return new ReidentifyFileRequestBuilder(); + } + + /** + * @return How {@code value} supplies the file. Required. + */ + public DataSourceType getDataSource() { + return dataSource; + } + + /** + * @return Base64 content, skyflow id or presigned URL of the file, according to {@code dataSource}. Required. + */ + public String getValue() { + return value; + } + + /** + * @return Format of the file. Optional; the API infers it when omitted. Re-identification supports text formats + * such as TXT, CSV, JSON, JSONL, XML, DOCX, XLSX and PPTX. + */ + public FileDataFormat getDataFormat() { + return dataFormat; + } + + /** + * @return Optional per-group rendering rules. + */ + public List getRedactionLevel() { + return redactionLevel; + } + + @Override + public String toString() { + return "ReidentifyFileRequest{" + + "dataSource=" + dataSource + + ", value=" + value + + ", dataFormat=" + dataFormat + + ", redactionLevel=" + redactionLevel + + '}'; + } + + public static final class ReidentifyFileRequestBuilder { + private DataSourceType dataSource; + private String value; + private FileDataFormat dataFormat; + private List redactionLevel; + + private ReidentifyFileRequestBuilder() { + } + + /** + * @param dataSource How {@code value} supplies the file. Required. + * @return this builder + */ + public ReidentifyFileRequestBuilder dataSource(DataSourceType dataSource) { + this.dataSource = dataSource; + return this; + } + + /** + * @param value Base64 content, skyflow id or presigned URL of the file, according to {@code dataSource}. Required. + * @return this builder + */ + public ReidentifyFileRequestBuilder value(String value) { + this.value = value; + return this; + } + + /** + * @param dataFormat Format of the file. Optional. + * @return this builder + */ + public ReidentifyFileRequestBuilder dataFormat(FileDataFormat dataFormat) { + this.dataFormat = dataFormat; + return this; + } + + /** + * @param redactionLevel Optional per-group rendering rules. + * @return this builder + */ + public ReidentifyFileRequestBuilder redactionLevel(List redactionLevel) { + this.redactionLevel = redactionLevel; + return this; + } + + public ReidentifyFileRequest build() { + return new ReidentifyFileRequest(this); + } + } +} diff --git a/flowvault/src/main/java/com/skyflow/detect/ReidentifyFileResponse.java b/flowvault/src/main/java/com/skyflow/detect/ReidentifyFileResponse.java new file mode 100644 index 00000000..17d32f3e --- /dev/null +++ b/flowvault/src/main/java/com/skyflow/detect/ReidentifyFileResponse.java @@ -0,0 +1,59 @@ +package com.skyflow.detect; + +import java.util.List; + +/** + * Response of {@code reidentifyFile}. File re-identification is synchronous, so a {@code SUCCESS} + * status carries the output in the same response. + */ +public final class ReidentifyFileResponse { + private final DetectRunStatus status; + private final DataSourceType outputType; + private final List output; + private final Metrics metrics; + + public ReidentifyFileResponse(DetectRunStatus status, DataSourceType outputType, List output, Metrics metrics) { + this.status = status; + this.outputType = outputType; + this.output = output; + this.metrics = metrics; + } + + /** + * @return Processing status. UNKNOWN when the API did not report one. + */ + public DetectRunStatus getStatus() { + return status; + } + + /** + * @return How output files are delivered, or null when not reported. + */ + public DataSourceType getOutputType() { + return outputType; + } + + /** + * @return Output artifacts. Empty when none were returned, never null. + */ + public List getOutput() { + return output; + } + + /** + * @return Processing metrics, or null when not reported. + */ + public Metrics getMetrics() { + return metrics; + } + + @Override + public String toString() { + return "ReidentifyFileResponse{" + + "status=" + status + + ", outputType=" + outputType + + ", output=" + output + + ", metrics=" + metrics + + '}'; + } +} diff --git a/flowvault/src/main/java/com/skyflow/detect/internal/ReidentifyFileMapper.java b/flowvault/src/main/java/com/skyflow/detect/internal/ReidentifyFileMapper.java new file mode 100644 index 00000000..d1653166 --- /dev/null +++ b/flowvault/src/main/java/com/skyflow/detect/internal/ReidentifyFileMapper.java @@ -0,0 +1,69 @@ +package com.skyflow.detect.internal; + +import com.skyflow.detect.DataSourceType; +import com.skyflow.detect.DetectRunStatus; +import com.skyflow.detect.FileOutput; +import com.skyflow.detect.ReidentifyFileRequest; +import com.skyflow.detect.ReidentifyFileResponse; +import com.skyflow.generated.detect.rest.resources.filesv2.requests.ReidentifyFileRequestV2; +import com.skyflow.generated.detect.rest.resources.filesv2.types.ReidentifyFileRequestV2DataFormat; +import com.skyflow.generated.detect.rest.resources.filesv2.types.ReidentifyFileRequestV2DataSource; +import com.skyflow.generated.detect.rest.types.ReidentifyFileResponseV2; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Converts the flowvault {@code reidentifyFile} models to and from the generated V2 API types. + * Internal to the SDK. + */ +public final class ReidentifyFileMapper { + private ReidentifyFileMapper() { + } + + public static ReidentifyFileRequestV2 toApiRequest(ReidentifyFileRequest request, String vaultId) { + ReidentifyFileRequestV2._FinalStage builder = ReidentifyFileRequestV2.builder() + .dataSource(ReidentifyFileRequestV2DataSource.valueOf(request.getDataSource().name())) + .value(request.getValue()) + .vaultId(vaultId); + if (request.getDataFormat() != null) { + // The wire value for a file format is the lower-case extension. + builder.dataFormat(ReidentifyFileRequestV2DataFormat.valueOf(request.getDataFormat().name().toLowerCase())); + } + if (request.getRedactionLevel() != null) { + builder.redactionLevel(DetectRequestMapper.toRedactionLevels(request.getRedactionLevel())); + } + return builder.build(); + } + + public static ReidentifyFileResponse toResponse(ReidentifyFileResponseV2 response) { + List outputs = new ArrayList<>(); + List apiOutputs = response.getOutput().orElse(null); + if (apiOutputs != null) { + for (com.skyflow.generated.detect.rest.types.FileOutput apiOutput : apiOutputs) { + if (apiOutput != null) { + outputs.add(DetectResponseMapper.toFileOutput(apiOutput)); + } + } + } + return new ReidentifyFileResponse( + response.getStatus().map(s -> enumOrDefault(DetectRunStatus.class, s.toString(), DetectRunStatus.UNKNOWN)) + .orElse(DetectRunStatus.UNKNOWN), + response.getOutputType().map(t -> enumOrDefault(DataSourceType.class, t.toString(), null)).orElse(null), + Collections.unmodifiableList(outputs), + response.getMetrics().map(DetectResponseMapper::toMetrics).orElse(null)); + } + + /** Maps a wire value onto a public enum, tolerating values this SDK version does not know. */ + private static > E enumOrDefault(Class type, String value, E fallback) { + if (value == null) { + return fallback; + } + try { + return Enum.valueOf(type, value); + } catch (IllegalArgumentException unknown) { + return fallback; + } + } +} diff --git a/flowvault/src/main/java/com/skyflow/detect/internal/ReidentifyFileValidations.java b/flowvault/src/main/java/com/skyflow/detect/internal/ReidentifyFileValidations.java new file mode 100644 index 00000000..0eafdcc8 --- /dev/null +++ b/flowvault/src/main/java/com/skyflow/detect/internal/ReidentifyFileValidations.java @@ -0,0 +1,43 @@ +package com.skyflow.detect.internal; + +import com.skyflow.detect.ReidentifyFileRequest; +import com.skyflow.errors.ErrorMessage; +import com.skyflow.errors.ErrorCode; +import com.skyflow.errors.SkyflowException; +import com.skyflow.logs.ErrorLogs; +import com.skyflow.utils.BaseUtils; +import com.skyflow.utils.logger.LogUtil; + + +/** + * Client-side validation for the flowvault {@code reidentifyFile} request. Internal to the SDK. + */ +public final class ReidentifyFileValidations { + private static final String INTERFACE_NAME = "detect"; + + private ReidentifyFileValidations() { + } + + public static void validateReidentifyFileRequest(ReidentifyFileRequest request) throws SkyflowException { + if (request == null) { + throw invalid(ErrorLogs.REIDENTIFY_FILE_REQUEST_NULL, ErrorMessage.ReidentifyFileRequestNull); + } + if (request.getDataSource() == null) { + throw invalid(ErrorLogs.INVALID_DATA_SOURCE_IN_REIDENTIFY_FILE, ErrorMessage.InvalidDataSourceInReidentifyFile); + } + if (isBlank(request.getValue())) { + throw invalid(ErrorLogs.INVALID_VALUE_IN_REIDENTIFY_FILE, ErrorMessage.InvalidValueInReidentifyFile); + } + // redactionLevel entries are optional on the API and are passed through as given. + } + + + private static boolean isBlank(String value) { + return value == null || value.trim().isEmpty(); + } + + private static SkyflowException invalid(ErrorLogs log, ErrorMessage message) { + LogUtil.printErrorLog(BaseUtils.parameterizedString(log.getLog(), INTERFACE_NAME)); + return new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), message.getMessage()); + } +} diff --git a/flowvault/src/main/java/com/skyflow/errors/ErrorMessage.java b/flowvault/src/main/java/com/skyflow/errors/ErrorMessage.java new file mode 100644 index 00000000..cceec8b8 --- /dev/null +++ b/flowvault/src/main/java/com/skyflow/errors/ErrorMessage.java @@ -0,0 +1,77 @@ +package com.skyflow.errors; + +import com.skyflow.utils.Constants; + +/** + * Validation messages specific to the flowvault SDK. Messages shared by every SDK live in + * {@link BaseErrorMessage} in common; this enum holds only what common and skyvault do not use. + */ +public enum ErrorMessage { + // Vault config + EmptyVaultUrl("%s0 Initialization failed. Vault URL is empty. Specify a valid vault URL."), + InvalidVaultUrlFormat("%s0 Initialization failed. Vault URL must start with 'https://'."), + EitherVaultUrlOrClusterIdRequired("%s0 Initialization failed. Specify either 'clusterId' or 'vaultURL'."), + // Insert / upsert + TableSpecifiedInRequestAndRecordObject("%s0 Validation error. Table name cannot be specified at both the request and record levels. Please specify the table name in only one place."), + TableNotSpecifiedInRequestAndRecordObject("%s0 Validation error. Table name is missing. Table name should be specified at one place either at the request level or record level. Please specify the table name at one place."), + UpsertTableRequestAtRecordLevel("%s0 Validation error. Table name should be present at each record level when upsert is present at record level."), + UpsertTableRequestAtRequestLevel("%s0 Validation error. Upsert should be present at each record level when table name is present at record level."), + EmptyRecords("%s0 Validation error. 'records' can't be empty. Specify records."), + EmptyKeyInRecords("%s0 Validation error. Invalid key in data in records. Specify a valid key."), + EmptyValueInRecords("%s0 Validation error. Invalid value in records. Specify a valid value."), + RecordsKeyError("%s0 Validation error. 'records' key is missing from the payload. Specify a 'records' key."), + InvalidUpsertUpdateType("%s0 Validation error. Invalid upsert updateType. Specify either 'UPDATE' or 'REPLACE'."), + EmptyUpsertValues("%s0 Validation error. Upsert column values can't be empty. Specify at least one upsert column."), + RecordSizeExceedError("%s0 Maximum number of records exceeded. The limit is 100000."), + InvalidRecord("%s0 Validation error. InsertRecord object in the list is invalid. Specify a valid InsertRecord object."), + BothSingleTableFieldsAndRecordsSpecified("%s0 Validation error. Both single-table lookup fields ('table', 'ids', 'fields', 'uniqueValues', 'columnRedactions') and 'records' can't be specified. Either specify single-table fields or 'records'."), + // Detokenize / delete tokens + EmptyValues("%s0 Validation error. 'values' can't be empty. Specify values."), + EmptyValueInValues("%s0 Validation error. Invalid value in values. Specify a valid value."), + EmptyValueInTokens("%s0 Validation error. Invalid value in tokens. Specify a valid value."), + TokensSizeExceedError("%s0 Maximum number of tokens exceeded. The limit is 100000."), + DeleteTokensRequestNull("%s0 Validation error. DeleteTokensRequest object is null. Specify a valid DeleteTokensRequest object."), + EmptyDeleteTokensData("%s0 Validation error. Tokens list is empty. Specify at least one token to delete."), + EmptyTokenInDeleteTokensData("%s0 Validation error. Invalid token in delete tokens request. Specify a valid token."), + DeleteTokensSizeExceedError("%s0 Maximum number of tokens exceeded. The limit is 100000."), + NullTokenGroupRedactions("%s0 Validation error. TokenGroupRedaction in the list is null. Specify a valid TokenGroupRedactions object."), + NullRedactionInTokenGroup("%s0 Validation error. Redaction in TokenGroupRedactions is null or empty. Specify a valid redaction."), + NullTokenGroupNameInTokenGroup("%s0 Validation error. TokenGroupName in TokenGroupRedactions is null or empty. Specify a valid tokenGroupName."), + // Get / update + IdsOrUniqueValuesKeyError("%s0 Validation error. 'ids' or 'uniqueValues' key is missing from the payload. Specify ids or uniqueValues in payload."), + BothIdsAndUniqueValuesSpecified("%s0 Validation error. Both Skyflow IDs and unique values can't be specified. Either specify Skyflow IDs or unique values."), + EmptyUniqueValues("%s0 Validation error. 'uniqueValues' can't be empty. Specify at least one unique value."), + EmptyUniqueValueInUniqueValues("%s0 Validation error. Invalid unique value in 'uniqueValues'. Specify a valid unique value."), + NullColumnRedactions("%s0 Validation error. Column redaction object can not be null. Specify a valid column redaction object."), + NullColumnNameInColumnRedaction("%s0 Validation error. Column name can not be null or empty in column redaction. Specify a valid column name."), + NullRedactionInColumnRedaction("%s0 Validation error. Redaction can not be null or empty in column redaction. Specify a valid redaction."), + NullGetRecordRequest("%s0 Validation error. Record in 'records' is null. Specify a valid record."), + UpdateRecordNull("%s0 Validation error. UpdateRequestRecord object in the list is null. Specify a valid UpdateRequestRecord object."), + RecordSkyflowIdKeyError("%s0 Validation error. 'skyflowId' key is missing from the record. Specify a 'skyflowId' key."), + EmptySkyflowIdInRecord("%s0 Validation error. 'skyflowId' can't be empty in the record. Specify a valid skyflow ID."), + // Tokenize + EmptyTokenizeData("%s0 Validation error. Tokenize data is empty. Specify at least one tokenize record."), + TokenizeRecordNull("%s0 Validation error. TokenizeRecord in the list is null. Specify a valid TokenizeRecord object."), + InvalidBulkTokenizeRecordType("%s0 Validation error. Record in the list must be of type BulkTokenizeRequestRecord. Specify a valid BulkTokenizeRequestRecord object."), + EmptyValueInTokenizeRecord("%s0 Validation error. Value in TokenizeRecord is null or empty. Specify a valid value."), + EmptyTokenGroupNamesInTokenizeRecord("%s0 Validation error. TokenGroupNames in TokenizeRecord is null or empty. Specify at least one token group name."), + EmptyTokenGroupNameInTokenizeRecord("%s0 Validation error. Token group name in TokenizeRecord is null or empty. Specify a valid token group name."), + TokenizeDataSizeExceedError("%s0 Maximum number of tokenize records exceeded. The limit is 100000."), + MissingIndexInBulkTokenizeRecord("%s0 Validation error. Index in BulkTokenizeRequestRecord is null. Specify an index for every record."), + DuplicateIndexInBulkTokenizeRecord("%s0 Validation error. Duplicate index in BulkTokenizeRequestRecord. Specify a unique index for every record."), + // Detect (flowvault only) + ReidentifyFileRequestNull("%s0 Validation error. ReidentifyFile request cannot be null."), + InvalidDataSourceInReidentifyFile("%s0 Validation error. Invalid dataSource in reidentify file request. Specify BASE64, SKYFLOW_ID or PRESIGNED_URL."), + InvalidValueInReidentifyFile("%s0 Validation error. Invalid value in reidentify file request. Specify the non-empty base64 content, skyflow id or presigned URL of the file."), + ; + + private final String message; + + ErrorMessage(String message) { + this.message = message.replace("%s0", Constants.SDK_PREFIX); + } + + public String getMessage() { + return message; + } +} diff --git a/flowvault/src/main/java/com/skyflow/utils/validations/Validations.java b/flowvault/src/main/java/com/skyflow/utils/validations/Validations.java index 084b01d7..257685f1 100644 --- a/flowvault/src/main/java/com/skyflow/utils/validations/Validations.java +++ b/flowvault/src/main/java/com/skyflow/utils/validations/Validations.java @@ -5,6 +5,7 @@ import com.skyflow.enums.InterfaceName; import com.skyflow.generated.rest.types.FlowEnumUpdateType; import com.skyflow.errors.ErrorCode; +import com.skyflow.errors.BaseErrorMessage; import com.skyflow.errors.ErrorMessage; import com.skyflow.errors.SkyflowException; import com.skyflow.logs.ErrorLogs; @@ -42,38 +43,38 @@ public static void validateCredentials(Credentials credentials) throws SkyflowEx if (nonNullMembers > 1) { LogUtil.printErrorLog(ErrorLogs.MULTIPLE_TOKEN_GENERATION_MEANS_PASSED.getLog()); throw new SkyflowException( - ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.MultipleTokenGenerationMeansPassed.getMessage() + ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.MultipleTokenGenerationMeansPassed.getMessage() ); } else if (nonNullMembers < 1) { LogUtil.printErrorLog(ErrorLogs.NO_TOKEN_GENERATION_MEANS_PASSED.getLog()); throw new SkyflowException( - ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.NoTokenGenerationMeansPassed.getMessage() + ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.NoTokenGenerationMeansPassed.getMessage() ); } else if (path != null && path.trim().isEmpty()) { LogUtil.printErrorLog(ErrorLogs.EMPTY_CREDENTIALS_PATH.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyCredentialFilePath.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.EmptyCredentialFilePath.getMessage()); } else if (credentialsString != null && credentialsString.trim().isEmpty()) { LogUtil.printErrorLog(ErrorLogs.EMPTY_CREDENTIALS_STRING.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyCredentialsString.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.EmptyCredentialsString.getMessage()); } else if (token != null && token.trim().isEmpty()) { LogUtil.printErrorLog(ErrorLogs.EMPTY_TOKEN_VALUE.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyToken.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.EmptyToken.getMessage()); } else if (apiKey != null) { if (apiKey.trim().isEmpty()) { LogUtil.printErrorLog(ErrorLogs.EMPTY_API_KEY_VALUE.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyApikey.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.EmptyApikey.getMessage()); } else { Pattern pattern = Pattern.compile(Constants.API_KEY_REGEX); Matcher matcher = pattern.matcher(apiKey); if (!matcher.matches()) { LogUtil.printErrorLog(ErrorLogs.INVALID_API_KEY.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.InvalidApikey.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.InvalidApikey.getMessage()); } } } else if (roles != null) { if (roles.isEmpty()) { LogUtil.printErrorLog(ErrorLogs.EMPTY_ROLES.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyRoles.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.EmptyRoles.getMessage()); } else { for (int index = 0; index < roles.size(); index++) { String role = roles.get(index); @@ -81,7 +82,7 @@ public static void validateCredentials(Credentials credentials) throws SkyflowEx LogUtil.printErrorLog(Utils.parameterizedString( ErrorLogs.EMPTY_OR_NULL_ROLE_IN_ROLES.getLog(), Integer.toString(index) )); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyRoleInRoles.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.EmptyRoleInRoles.getMessage()); } } } @@ -91,13 +92,13 @@ public static void validateCredentials(Credentials credentials) throws SkyflowEx String ctxStr = (String) context; if (ctxStr.trim().isEmpty()) { LogUtil.printErrorLog(ErrorLogs.EMPTY_OR_NULL_CONTEXT.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyContext.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.EmptyContext.getMessage()); } } else if (context instanceof Map) { Map ctxMap = (Map) context; if (ctxMap.isEmpty()) { LogUtil.printErrorLog(ErrorLogs.EMPTY_OR_NULL_CONTEXT.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyContext.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.EmptyContext.getMessage()); } Pattern ctxKeyPattern = Pattern.compile(Constants.CONTEXT_KEY_REGEX); for (Object key : ctxMap.keySet()) { @@ -106,12 +107,12 @@ public static void validateCredentials(Credentials credentials) throws SkyflowEx LogUtil.printErrorLog(Utils.parameterizedString( ErrorLogs.INVALID_CONTEXT_MAP_KEY.getLog(), keyStr)); throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), - Utils.parameterizedString(ErrorMessage.InvalidContextMapKey.getMessage(), keyStr)); + Utils.parameterizedString(BaseErrorMessage.InvalidContextMapKey.getMessage(), keyStr)); } } } else { LogUtil.printErrorLog(ErrorLogs.INVALID_CONTEXT_TYPE.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.InvalidContextType.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.InvalidContextType.getMessage()); } } } @@ -120,7 +121,7 @@ public static void validateCredentials(Credentials credentials) throws SkyflowEx public static void validateVaultConfiguration(VaultConfig vaultConfig) throws SkyflowException { if (vaultConfig == null) { LogUtil.printErrorLog(ErrorLogs.VAULT_CONFIG_IS_NULL.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.NullVaultConfig.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.NullVaultConfig.getMessage()); } String vaultId = vaultConfig.getVaultId(); String clusterId = vaultConfig.getClusterId(); @@ -129,10 +130,10 @@ public static void validateVaultConfiguration(VaultConfig vaultConfig) throws Sk if (vaultId == null) { LogUtil.printErrorLog(ErrorLogs.VAULT_ID_IS_REQUIRED.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.InvalidVaultId.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.InvalidVaultId.getMessage()); } else if (vaultId.trim().isEmpty()) { LogUtil.printErrorLog(ErrorLogs.EMPTY_VAULT_ID.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyVaultId.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.EmptyVaultId.getMessage()); } else if (credentials != null) { validateCredentials(credentials); } @@ -153,7 +154,7 @@ public static void validateVaultConfiguration(VaultConfig vaultConfig) throws Sk throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EitherVaultUrlOrClusterIdRequired.getMessage()); } else if (clusterId.trim().isEmpty()) { LogUtil.printErrorLog(ErrorLogs.EMPTY_CLUSTER_ID.getLog()); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyClusterId.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.EmptyClusterId.getMessage()); } } } @@ -164,7 +165,7 @@ public static void validateInsertRequest(InsertRequest insertRequest) throws Sky LogUtil.printErrorLog(Utils.parameterizedString( ErrorLogs.INSERT_REQUEST_NULL.getLog(), InterfaceName.INSERT.getName() )); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.InsertRequestNull.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.InsertRequestNull.getMessage()); } List records = insertRequest.getRecords(); if (records == null) { @@ -225,14 +226,14 @@ private static void validateInsertRecordTokens(Map tokens) throw LogUtil.printErrorLog(Utils.parameterizedString( ErrorLogs.EMPTY_TOKENS.getLog(), InterfaceName.INSERT.getName() )); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyTokens.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.EmptyTokens.getMessage()); } for (String key : tokens.keySet()) { if (key == null || key.trim().isEmpty()) { LogUtil.printErrorLog(Utils.parameterizedString( ErrorLogs.EMPTY_OR_NULL_KEY_IN_TOKENS.getLog(), InterfaceName.INSERT.getName() )); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyKeyInTokens.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.EmptyKeyInTokens.getMessage()); } Object value = tokens.get(key); if (value == null || value.toString().trim().isEmpty()) { @@ -332,14 +333,14 @@ public static void validateDetokenizeRequest(DetokenizeRequest request) throws S LogUtil.printErrorLog(Utils.parameterizedString( ErrorLogs.DETOKENIZE_REQUEST_NULL.getLog(), InterfaceName.DETOKENIZE.getName() )); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.DetokenizeRequestNull.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.DetokenizeRequestNull.getMessage()); } List tokens = request.getTokens(); if (tokens == null || tokens.isEmpty()) { LogUtil.printErrorLog(Utils.parameterizedString( ErrorLogs.EMPTY_DETOKENIZE_DATA.getLog(), InterfaceName.DETOKENIZE.getName() )); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyDetokenizeData.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.EmptyDetokenizeData.getMessage()); } for (int index = 0; index < tokens.size(); index++) { @@ -349,7 +350,7 @@ public static void validateDetokenizeRequest(DetokenizeRequest request) throws S ErrorLogs.EMPTY_OR_NULL_TOKEN_IN_DETOKENIZE_DATA.getLog(), InterfaceName.DETOKENIZE.getName(), String.valueOf(index))); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyTokenInDetokenizeData.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.EmptyTokenInDetokenizeData.getMessage()); } } @@ -418,7 +419,7 @@ public static void validateDeleteRequest(DeleteRequest deleteRequest) throws Sky LogUtil.printErrorLog(Utils.parameterizedString( ErrorLogs.DELETE_REQUEST_NULL.getLog(), InterfaceName.DELETE_RECORDS.getName() )); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.DeleteRequestNull.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.DeleteRequestNull.getMessage()); } validateTableRequired(deleteRequest.getTableName(), InterfaceName.DELETE_RECORDS); validateIdsOrUniqueValues(deleteRequest.getSkyflowIds(), deleteRequest.getUniqueValues(), InterfaceName.DELETE_RECORDS); @@ -458,19 +459,19 @@ public static void validateUpdateRequest(UpdateRequest updateRequest) throws Sky LogUtil.printErrorLog(Utils.parameterizedString( ErrorLogs.UPDATE_REQUEST_NULL.getLog(), InterfaceName.UPDATE.getName() )); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.UpdateRequestNull.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.UpdateRequestNull.getMessage()); } String tableName = updateRequest.getTableName(); if (tableName == null) { LogUtil.printErrorLog(Utils.parameterizedString( ErrorLogs.TABLE_IS_REQUIRED.getLog(), InterfaceName.UPDATE.getName() )); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.TableKeyError.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.TableKeyError.getMessage()); } else if (tableName.trim().isEmpty()) { LogUtil.printErrorLog(Utils.parameterizedString( ErrorLogs.EMPTY_TABLE_NAME.getLog(), InterfaceName.UPDATE.getName() )); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyTable.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.EmptyTable.getMessage()); } List records = updateRequest.getRecords(); @@ -532,14 +533,14 @@ public static void validateUpdateRequest(UpdateRequest updateRequest) throws Sky LogUtil.printErrorLog(Utils.parameterizedString( ErrorLogs.EMPTY_TOKENS.getLog(), InterfaceName.UPDATE.getName() )); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyTokens.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.EmptyTokens.getMessage()); } for (String key : tokens.keySet()) { if (key == null || key.trim().isEmpty()) { LogUtil.printErrorLog(Utils.parameterizedString( ErrorLogs.EMPTY_OR_NULL_KEY_IN_TOKENS.getLog(), InterfaceName.UPDATE.getName() )); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyKeyInTokens.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.EmptyKeyInTokens.getMessage()); } Object value = tokens.get(key); if (value == null || value.toString().trim().isEmpty()) { @@ -559,7 +560,7 @@ public static void validateGetRequest(GetRequest getRequest) throws SkyflowExcep LogUtil.printErrorLog(Utils.parameterizedString( ErrorLogs.GET_REQUEST_NULL.getLog(), InterfaceName.GET.getName() )); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.GetRequestNull.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.GetRequestNull.getMessage()); } boolean hasSingleTableFields = hasText(getRequest.getTableName()) @@ -603,12 +604,12 @@ private static void validateTableRequired(String table, InterfaceName interfaceN LogUtil.printErrorLog(Utils.parameterizedString( ErrorLogs.TABLE_IS_REQUIRED.getLog(), interfaceName.getName() )); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.TableKeyError.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.TableKeyError.getMessage()); } else if (table.trim().isEmpty()) { LogUtil.printErrorLog(Utils.parameterizedString( ErrorLogs.EMPTY_TABLE_NAME.getLog(), interfaceName.getName() )); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyTable.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.EmptyTable.getMessage()); } } @@ -638,7 +639,7 @@ private static void validateIdsOrUniqueValues(List ids, List fields) throws SkyflowException LogUtil.printErrorLog(Utils.parameterizedString( ErrorLogs.EMPTY_FIELDS.getLog(), InterfaceName.GET.getName() )); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyFields.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.EmptyFields.getMessage()); } for (int index = 0; index < fields.size(); index++) { String field = fields.get(index); @@ -671,7 +672,7 @@ private static void validateFields(List fields) throws SkyflowException LogUtil.printErrorLog(Utils.parameterizedString( ErrorLogs.EMPTY_OR_NULL_FIELD_IN_FIELDS.getLog(), InterfaceName.GET.getName(), String.valueOf(index) )); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyFieldInFields.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.EmptyFieldInFields.getMessage()); } } } @@ -711,7 +712,7 @@ public static void validateBulkTokenizeRequest(BulkTokenizeRequest request) thro LogUtil.printErrorLog(Utils.parameterizedString( ErrorLogs.TOKENIZE_REQUEST_NULL.getLog(), InterfaceName.TOKENIZE.getName() )); - throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.TokenizeRequestNull.getMessage()); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseErrorMessage.TokenizeRequestNull.getMessage()); } List records = request.getRecords(); if (records == null || records.isEmpty()) { diff --git a/flowvault/src/main/java/com/skyflow/vault/controller/DetectController.java b/flowvault/src/main/java/com/skyflow/vault/controller/DetectController.java new file mode 100644 index 00000000..3f86640d --- /dev/null +++ b/flowvault/src/main/java/com/skyflow/vault/controller/DetectController.java @@ -0,0 +1,183 @@ +package com.skyflow.vault.controller; + +import com.skyflow.config.Credentials; +import com.skyflow.config.VaultConfig; +import com.skyflow.detect.DeidentifyFileRequest; +import com.skyflow.detect.DeidentifyFileResponse; +import com.skyflow.detect.GetRunRequest; +import com.skyflow.detect.ReidentifyFileRequest; +import com.skyflow.detect.ReidentifyFileResponse; +import com.skyflow.detect.internal.DeidentifyFileFlow; +import com.skyflow.detect.internal.DetectRequestMapper; +import com.skyflow.detect.internal.DetectValidations; +import com.skyflow.detect.internal.ReidentifyFileMapper; +import com.skyflow.detect.internal.ReidentifyFileValidations; +import com.skyflow.errors.SkyflowException; +import com.skyflow.generated.detect.rest.core.ApiClientApiException; +import com.skyflow.generated.detect.rest.core.ApiClientException; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import com.skyflow.generated.detect.rest.core.RequestOptions; +import com.skyflow.generated.detect.rest.resources.filesv2.requests.DeidentifyFileRequestV2; +import com.skyflow.generated.detect.rest.resources.filesv2.requests.ReidentifyFileRequestV2; +import com.skyflow.generated.detect.rest.types.DeidentifyFileResponseV2; +import com.skyflow.generated.detect.rest.types.ReidentifyFileResponseV2; +import com.skyflow.logs.ErrorLogs; +import com.skyflow.logs.InfoLogs; +import com.skyflow.utils.BaseConstants; +import com.skyflow.utils.Utils; +import com.skyflow.utils.logger.LogUtil; + +/** + * Skyflow Detect operations for a flowvault vault, returned by {@code Skyflow.detect()} and + * {@code Skyflow.detect(vaultId)}. The shared Detect V2 methods are inherited from + * {@link BaseDetectController}; this class adds the flowvault-only {@link #deidentifyFile} and + * {@link #reidentifyFile} and + * supplies the flowvault URL rules, SDK metrics and the vault-level timeout and retry overrides + * from {@link VaultConfig}. + */ +public final class DetectController extends BaseDetectController { + private static final int DEFAULT_TIMEOUT_SECONDS = 60; + private static final int DEFAULT_MAX_RETRIES = 0; + private static final long DEFAULT_INITIAL_RETRY_DELAY_MILLIS = 500L; + private static final long DEFAULT_MAX_RETRY_DELAY_MILLIS = 2000L; + + public DetectController(VaultConfig vaultConfig, Credentials credentials) { + super(vaultConfig, credentials); + } + + /** Same precedence as the vault controller: environment variable, explicit vaultUrl, then cluster id and env. */ + @Override + protected String resolveVaultUrl() throws SkyflowException { + String vaultUrl = Utils.getEnvVaultUrl(); + if (vaultUrl == null || vaultUrl.isEmpty()) { + vaultUrl = getVaultConfig().getVaultUrl(); + } + if (vaultUrl == null || vaultUrl.isEmpty()) { + vaultUrl = Utils.getVaultUrl(getVaultConfig().getClusterId(), getVaultConfig().getEnv()); + } + return vaultUrl; + } + + @Override + protected String getSdkMetrics() { + return Utils.getMetrics().toString(); + } + + @Override + protected Integer effectiveTimeoutSeconds() { + return firstNonNull(getVaultConfig().getTimeout(), super.effectiveTimeoutSeconds(), DEFAULT_TIMEOUT_SECONDS); + } + + @Override + protected Integer effectiveMaxRetries() { + return firstNonNull(getVaultConfig().getMaxRetries(), super.effectiveMaxRetries(), DEFAULT_MAX_RETRIES); + } + + @Override + protected Long effectiveInitialRetryDelayMillis() { + return firstNonNull(getVaultConfig().getInitialRetryDelayMillis(), super.effectiveInitialRetryDelayMillis(), + DEFAULT_INITIAL_RETRY_DELAY_MILLIS); + } + + @Override + protected Long effectiveMaxRetryDelayMillis() { + return firstNonNull(getVaultConfig().getMaxRetryDelayMillis(), super.effectiveMaxRetryDelayMillis(), + DEFAULT_MAX_RETRY_DELAY_MILLIS); + } + + // ------------------------------------------------------------------ + // Flowvault-only Detect V2 operations + // ------------------------------------------------------------------ + + /** + * Detects and de-identifies sensitive data in a file. The API is asynchronous: by default this + * submits the run and returns its id for use with {@link #getRun}. With + * {@link DeidentifyFileRequest#getPollOptions() pollOptions} the SDK polls the run with exponential + * backoff and returns the completed run, or the run id with a non-terminal status if the budget + * runs out. On success, a request built from a local file has its outputs written to disk. + * + * @param request the file (base64, skyflow id, presigned URL, or a local file) plus a stored or inline configuration + * @return run id, and the run's status, outputs and metrics when polled + * @throws SkyflowException on validation failure, credential failure, an API error or an output write failure + */ + public DeidentifyFileResponse deidentifyFile(DeidentifyFileRequest request) throws SkyflowException { + LogUtil.printInfoLog(InfoLogs.DEIDENTIFY_FILE_TRIGGERED.getLog()); + LogUtil.printInfoLog(InfoLogs.VALIDATE_DEIDENTIFY_FILE_REQUEST.getLog()); + DetectValidations.validateDeidentifyFileRequest(request); + String token = resolveToken(); + DeidentifyFileRequestV2 apiRequest = DetectRequestMapper.toDeidentifyFileRequest(request, getVaultConfig().getVaultId()); + String runId; + try { + DeidentifyFileResponseV2 apiResponse = filesClient().deidentifyFileV2(apiRequest, requestOptions(token)); + LogUtil.printInfoLog(InfoLogs.DEIDENTIFY_FILE_REQUEST_RESOLVED.getLog()); + runId = apiResponse.getRunId().orElse(null); + } catch (ApiClientApiException e) { + LogUtil.printErrorLog(ErrorLogs.DEIDENTIFY_FILE_REQUEST_REJECTED.getLog()); + throw new SkyflowException(e.statusCode(), e, e.headers(), responseBody(e.body())); + } catch (ApiClientException e) { + LogUtil.printErrorLog(ErrorLogs.DEIDENTIFY_FILE_REQUEST_REJECTED.getLog()); + throw new SkyflowException(e); + } + DeidentifyFileResponse response = DeidentifyFileFlow.complete(request, runId, + id -> getRun(GetRunRequest.builder().runId(id).build())); + LogUtil.printInfoLog(InfoLogs.DEIDENTIFY_FILE_SUCCESS.getLog()); + return response; + } + + /** + * Replaces tokens in a file with their original values or a redacted rendering. Synchronous: + * the re-identified file is returned in the response rather than through a run id. + * + * @param request the file (base64, skyflow id or presigned URL) plus optional per-group redaction rules + * @return status, output files and metrics + * @throws SkyflowException on validation failure, credential failure or an API error + */ + public ReidentifyFileResponse reidentifyFile(ReidentifyFileRequest request) throws SkyflowException { + LogUtil.printInfoLog(InfoLogs.REIDENTIFY_FILE_TRIGGERED.getLog()); + LogUtil.printInfoLog(InfoLogs.VALIDATE_REIDENTIFY_FILE_REQUEST.getLog()); + ReidentifyFileValidations.validateReidentifyFileRequest(request); + String token = resolveToken(); + ReidentifyFileRequestV2 apiRequest = ReidentifyFileMapper.toApiRequest(request, getVaultConfig().getVaultId()); + try { + ReidentifyFileResponseV2 apiResponse = filesClient().reidentifyFileV2(apiRequest, requestOptions(token)); + LogUtil.printInfoLog(InfoLogs.REIDENTIFY_FILE_REQUEST_RESOLVED.getLog()); + ReidentifyFileResponse response = ReidentifyFileMapper.toResponse(apiResponse); + LogUtil.printInfoLog(InfoLogs.REIDENTIFY_FILE_SUCCESS.getLog()); + return response; + } catch (ApiClientApiException e) { + LogUtil.printErrorLog(ErrorLogs.REIDENTIFY_FILE_REQUEST_REJECTED.getLog()); + throw new SkyflowException(e.statusCode(), e, e.headers(), responseBody(e.body())); + } catch (ApiClientException e) { + LogUtil.printErrorLog(ErrorLogs.REIDENTIFY_FILE_REQUEST_REJECTED.getLog()); + throw new SkyflowException(e); + } + } + + private RequestOptions requestOptions(String token) { + return RequestOptions.builder() + .addHeader("Authorization", "Bearer " + token) + .addHeader(BaseConstants.SDK_METRICS_HEADER_KEY, getSdkMetrics()) + .build(); + } + + private static String responseBody(Object body) { + if (body == null) { + return null; + } + if (body instanceof String) { + return (String) body; + } + try { + return ObjectMappers.JSON_MAPPER.writeValueAsString(body); + } catch (Exception ex) { + return body.toString(); + } + } + + private static T firstNonNull(T vaultLevel, T clientLevel, T defaultValue) { + if (vaultLevel != null) { + return vaultLevel; + } + return clientLevel != null ? clientLevel : defaultValue; + } +} diff --git a/flowvault/src/test/java/com/skyflow/detect/internal/ReidentifyFileMapperTests.java b/flowvault/src/test/java/com/skyflow/detect/internal/ReidentifyFileMapperTests.java new file mode 100644 index 00000000..88da01e4 --- /dev/null +++ b/flowvault/src/test/java/com/skyflow/detect/internal/ReidentifyFileMapperTests.java @@ -0,0 +1,182 @@ +package com.skyflow.detect.internal; + +import com.skyflow.detect.DataSourceType; +import com.skyflow.detect.DetectRunStatus; +import com.skyflow.detect.EntityType; +import com.skyflow.detect.FileDataFormat; +import com.skyflow.detect.FileOutput; +import com.skyflow.detect.RedactionLevel; +import com.skyflow.detect.RedactionType; +import com.skyflow.detect.ReidentifyFileRequest; +import com.skyflow.detect.ReidentifyFileResponse; +import com.skyflow.generated.detect.rest.core.ObjectMappers; +import com.skyflow.generated.detect.rest.resources.filesv2.requests.ReidentifyFileRequestV2; +import com.skyflow.generated.detect.rest.types.ReidentifyFileResponseV2; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; + +/** Request mapping is checked on the serialised wire JSON, response mapping from raw API JSON. */ +public class ReidentifyFileMapperTests { + + private static String wire(ReidentifyFileRequestV2 request) throws Exception { + return ObjectMappers.JSON_MAPPER.writeValueAsString(request); + } + + private static ReidentifyFileResponse fromJson(String json) throws Exception { + return ReidentifyFileMapper.toResponse(ObjectMappers.JSON_MAPPER.readValue(json, ReidentifyFileResponseV2.class)); + } + + // ─── request ────────────────────────────────────────────────────────────── + + @Test + public void mapsRequiredFieldsAndInjectsVaultId() throws Exception { + ReidentifyFileRequestV2 api = ReidentifyFileMapper.toApiRequest(ReidentifyFileRequest.builder() + .dataSource(DataSourceType.BASE64).value("W05BTUVfMV0=").build(), "vault-1"); + + Assert.assertEquals("BASE64", api.getDataSource().toString()); + Assert.assertEquals("W05BTUVfMV0=", api.getValue()); + Assert.assertEquals("vault-1", api.getVaultId()); + Assert.assertFalse(api.getDataFormat().isPresent()); + Assert.assertFalse(api.getRedactionLevel().isPresent()); + + String json = wire(api); + Assert.assertTrue(json, json.contains("\"dataSource\":\"BASE64\"")); + Assert.assertTrue(json, json.contains("\"value\":\"W05BTUVfMV0=\"")); + Assert.assertTrue(json, json.contains("\"vaultId\":\"vault-1\"")); + Assert.assertFalse(json, json.contains("dataFormat")); + Assert.assertFalse(json, json.contains("redactionLevel")); + } + + @Test + public void mapsEveryDataSource() throws Exception { + for (DataSourceType source : DataSourceType.values()) { + ReidentifyFileRequestV2 api = ReidentifyFileMapper.toApiRequest(ReidentifyFileRequest.builder() + .dataSource(source).value("x").build(), "v"); + Assert.assertEquals(source.name(), api.getDataSource().toString()); + Assert.assertTrue(wire(api).contains("\"dataSource\":\"" + source.name() + "\"")); + } + } + + @Test + public void dataFormatIsSentAsLowerCaseWireValue() throws Exception { + for (FileDataFormat format : new FileDataFormat[]{FileDataFormat.TXT, FileDataFormat.CSV, FileDataFormat.JSON, + FileDataFormat.JSONL, FileDataFormat.XML, FileDataFormat.DOCX, FileDataFormat.XLSX, FileDataFormat.PPTX}) { + ReidentifyFileRequestV2 api = ReidentifyFileMapper.toApiRequest(ReidentifyFileRequest.builder() + .dataSource(DataSourceType.SKYFLOW_ID).value("rec").dataFormat(format).build(), "v"); + Assert.assertEquals(format.name().toLowerCase(), api.getDataFormat().get().toString()); + Assert.assertTrue(wire(api).contains("\"dataFormat\":\"" + format.name().toLowerCase() + "\"")); + } + } + + @Test + public void mapsFlowvaultAndSkyvaultStyleRedactionLevels() throws Exception { + ReidentifyFileRequestV2 api = ReidentifyFileMapper.toApiRequest(ReidentifyFileRequest.builder() + .dataSource(DataSourceType.PRESIGNED_URL).value("https://s3/presigned") + .redactionLevel(Arrays.asList( + RedactionLevel.builder().tokenGroupName("names").redactionPattern("mask-middle").build(), + RedactionLevel.builder().entityName(EntityType.EMAIL_ADDRESS).redactionType(RedactionType.MASKED).build())) + .build(), "v"); + + Assert.assertEquals(2, api.getRedactionLevel().get().size()); + String json = wire(api); + Assert.assertTrue(json, json.contains("\"tokenGroupName\":\"names\"")); + Assert.assertTrue(json, json.contains("\"redactionPattern\":\"mask-middle\"")); + Assert.assertTrue(json, json.contains("\"entityName\":\"EMAIL_ADDRESS\"")); + Assert.assertTrue(json, json.contains("\"redactionType\":\"MASKED\"")); + // Unset halves of each level must not be serialised as nulls. + Assert.assertFalse(json, json.contains("null")); + } + + @Test + public void emptyRedactionLevelListIsSentAsEmptyArray() throws Exception { + ReidentifyFileRequestV2 api = ReidentifyFileMapper.toApiRequest(ReidentifyFileRequest.builder() + .dataSource(DataSourceType.BASE64).value("x").redactionLevel(Collections.emptyList()).build(), "v"); + Assert.assertTrue(api.getRedactionLevel().get().isEmpty()); + Assert.assertTrue(wire(api).contains("\"redactionLevel\":[]")); + } + + // ─── response ───────────────────────────────────────────────────────────── + + @Test + public void mapsFullResponse() throws Exception { + ReidentifyFileResponse response = fromJson("{\"status\":\"SUCCESS\",\"outputType\":\"PRESIGNED_URL\"," + + "\"output\":[{\"processedFile\":\"https://s3/out-1\",\"processedFileType\":\"REIDENTIFIED_FILE\",\"processedFileExtension\":\"docx\"}," + + "{\"processedFile\":\"https://s3/out-2\",\"processedFileType\":\"ENTITIES\",\"processedFileExtension\":\"json\"}]," + + "\"metrics\":{\"size\":12.5,\"wordCount\":120,\"characterCount\":800,\"pages\":2,\"slides\":0,\"duration\":1.25}}"); + + Assert.assertEquals(DetectRunStatus.SUCCESS, response.getStatus()); + Assert.assertEquals(DataSourceType.PRESIGNED_URL, response.getOutputType()); + Assert.assertEquals(2, response.getOutput().size()); + FileOutput first = response.getOutput().get(0); + Assert.assertEquals("https://s3/out-1", first.getProcessedFile()); + Assert.assertEquals("REIDENTIFIED_FILE", first.getProcessedFileType()); + Assert.assertEquals(FileDataFormat.DOCX, first.getProcessedFileExtension()); + Assert.assertEquals("ENTITIES", response.getOutput().get(1).getProcessedFileType()); + Assert.assertEquals(FileDataFormat.JSON, response.getOutput().get(1).getProcessedFileExtension()); + Assert.assertEquals(12.5, response.getMetrics().getSize(), 1e-9); + Assert.assertEquals(Integer.valueOf(120), response.getMetrics().getWordCount()); + Assert.assertEquals(Integer.valueOf(800), response.getMetrics().getCharacterCount()); + Assert.assertEquals(Integer.valueOf(2), response.getMetrics().getPages()); + Assert.assertEquals(Integer.valueOf(0), response.getMetrics().getSlides()); + Assert.assertEquals(1.25, response.getMetrics().getDuration(), 1e-9); + } + + @Test + public void mapsEveryKnownStatusAndOutputType() throws Exception { + for (DetectRunStatus status : DetectRunStatus.values()) { + Assert.assertEquals(status, fromJson("{\"status\":\"" + status.name() + "\"}").getStatus()); + } + for (DataSourceType type : DataSourceType.values()) { + Assert.assertEquals(type, fromJson("{\"outputType\":\"" + type.name() + "\"}").getOutputType()); + } + } + + @Test + public void emptyResponseYieldsDefaultsNotNulls() throws Exception { + ReidentifyFileResponse response = fromJson("{}"); + Assert.assertEquals(DetectRunStatus.UNKNOWN, response.getStatus()); + Assert.assertNull(response.getOutputType()); + Assert.assertNotNull(response.getOutput()); + Assert.assertTrue(response.getOutput().isEmpty()); + Assert.assertNull(response.getMetrics()); + } + + @Test + public void unknownWireValuesDegradeGracefully() throws Exception { + ReidentifyFileResponse response = fromJson("{\"status\":\"BRAND_NEW\",\"outputType\":\"FTP\"," + + "\"output\":[{\"processedFile\":\"x\",\"processedFileType\":\"NEW_KIND\",\"processedFileExtension\":\"heic\"}]}"); + Assert.assertEquals(DetectRunStatus.UNKNOWN, response.getStatus()); + Assert.assertNull(response.getOutputType()); + FileOutput output = response.getOutput().get(0); + Assert.assertEquals("x", output.getProcessedFile()); + Assert.assertEquals("NEW_KIND", output.getProcessedFileType()); + Assert.assertNull(output.getProcessedFileExtension()); + } + + @Test + public void nullOutputElementsAreSkipped() throws Exception { + Assert.assertEquals(1, fromJson("{\"output\":[null,{\"processedFile\":\"x\"},null]}").getOutput().size()); + } + + @Test + public void outputWithMissingFieldsMapsToNulls() throws Exception { + FileOutput output = fromJson("{\"output\":[{}]}").getOutput().get(0); + Assert.assertNull(output.getProcessedFile()); + Assert.assertNull(output.getProcessedFileType()); + Assert.assertNull(output.getProcessedFileExtension()); + } + + @Test + public void outputListIsUnmodifiable() throws Exception { + ReidentifyFileResponse response = fromJson("{\"output\":[{\"processedFile\":\"x\"}]}"); + try { + response.getOutput().clear(); + Assert.fail("expected UnsupportedOperationException"); + } catch (UnsupportedOperationException expected) { + Assert.assertEquals(1, response.getOutput().size()); + } + } +} diff --git a/flowvault/src/test/java/com/skyflow/detect/internal/ReidentifyFileValidationsTests.java b/flowvault/src/test/java/com/skyflow/detect/internal/ReidentifyFileValidationsTests.java new file mode 100644 index 00000000..5d1cc3fb --- /dev/null +++ b/flowvault/src/test/java/com/skyflow/detect/internal/ReidentifyFileValidationsTests.java @@ -0,0 +1,85 @@ +package com.skyflow.detect.internal; + +import com.skyflow.detect.DataSourceType; +import com.skyflow.detect.EntityType; +import com.skyflow.detect.RedactionLevel; +import com.skyflow.detect.RedactionType; +import com.skyflow.detect.ReidentifyFileRequest; +import com.skyflow.errors.ErrorCode; +import com.skyflow.errors.ErrorMessage; +import com.skyflow.errors.SkyflowException; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; + +public class ReidentifyFileValidationsTests { + + private static ReidentifyFileRequest.ReidentifyFileRequestBuilder valid() { + return ReidentifyFileRequest.builder().dataSource(DataSourceType.BASE64).value("Zm9v"); + } + + private static void assertRejected(ReidentifyFileRequest request, ErrorMessage expected) { + try { + ReidentifyFileValidations.validateReidentifyFileRequest(request); + Assert.fail("expected SkyflowException: " + expected); + } catch (SkyflowException e) { + Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); + Assert.assertEquals(expected.getMessage(), e.getMessage()); + } + } + + @Test + public void rejectsNullRequest() { + assertRejected(null, ErrorMessage.ReidentifyFileRequestNull); + } + + @Test + public void rejectsMissingDataSource() { + assertRejected(ReidentifyFileRequest.builder().value("Zm9v").build(), ErrorMessage.InvalidDataSourceInReidentifyFile); + } + + @Test + public void rejectsNullOrBlankValue() { + assertRejected(ReidentifyFileRequest.builder().dataSource(DataSourceType.BASE64).build(), ErrorMessage.InvalidValueInReidentifyFile); + assertRejected(valid().value("").build(), ErrorMessage.InvalidValueInReidentifyFile); + assertRejected(valid().value(" \t").build(), ErrorMessage.InvalidValueInReidentifyFile); + } + + @Test + public void checksFieldsInOrderDataSourceBeforeValue() { + // Both missing: dataSource is reported first. + assertRejected(ReidentifyFileRequest.builder().build(), ErrorMessage.InvalidDataSourceInReidentifyFile); + } + + @Test + public void acceptsMinimalAndFullValidRequests() throws SkyflowException { + ReidentifyFileValidations.validateReidentifyFileRequest(valid().build()); + ReidentifyFileValidations.validateReidentifyFileRequest(valid() + .dataSource(DataSourceType.PRESIGNED_URL).value("https://example.com/f") + .redactionLevel(Arrays.asList( + RedactionLevel.builder().tokenGroupName("g").redactionPattern("p").build(), + RedactionLevel.builder().entityName(EntityType.NAME).redactionType(RedactionType.REDACTED).build(), + RedactionLevel.builder().tokenGroupName("g2").redactionType(RedactionType.PLAIN_TEXT).build(), + RedactionLevel.builder().entityName(EntityType.SSN).redactionPattern("p2").build())) + .build()); + } + + @Test + public void nullOrEmptyRedactionLevelListIsAllowed() throws SkyflowException { + ReidentifyFileValidations.validateReidentifyFileRequest(valid().redactionLevel(null).build()); + ReidentifyFileValidations.validateReidentifyFileRequest(valid().redactionLevel(Collections.emptyList()).build()); + } + + @Test + public void anyRedactionLevelShapePassesThrough() throws SkyflowException { + // Every RedactionLevel field is optional on the API; the server applies its own rules. + ReidentifyFileValidations.validateReidentifyFileRequest(valid().redactionLevel(Arrays.asList( + null, + RedactionLevel.builder().build(), + RedactionLevel.builder().tokenGroupName("g").build(), + RedactionLevel.builder().tokenGroupName("g").entityName(EntityType.NAME) + .redactionPattern("p").redactionType(RedactionType.MASKED).build())).build()); + } +} diff --git a/flowvault/src/test/java/com/skyflow/utils/validations/ValidationsTests.java b/flowvault/src/test/java/com/skyflow/utils/validations/ValidationsTests.java index f900c03a..fb40b63c 100644 --- a/flowvault/src/test/java/com/skyflow/utils/validations/ValidationsTests.java +++ b/flowvault/src/test/java/com/skyflow/utils/validations/ValidationsTests.java @@ -15,6 +15,7 @@ import com.skyflow.config.VaultConfig; import com.skyflow.enums.Env; import com.skyflow.enums.UpdateType; +import com.skyflow.errors.BaseErrorMessage; import com.skyflow.errors.ErrorMessage; import com.skyflow.errors.SkyflowException; import com.skyflow.utils.Constants; @@ -232,7 +233,7 @@ public void testValidateVaultConfiguration_nullRequest() { Validations.validateVaultConfiguration(null); Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { - Assert.assertEquals(ErrorMessage.NullVaultConfig.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.NullVaultConfig.getMessage(), e.getMessage()); } } @@ -582,7 +583,7 @@ public void testValidateInsertRequest_emptyTokensMapThrows() { Validations.validateInsertRequest(request); Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { - Assert.assertEquals(ErrorMessage.EmptyTokens.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.EmptyTokens.getMessage(), e.getMessage()); } } @@ -597,7 +598,7 @@ public void testValidateInsertRequest_nullKeyInTokensThrows() { Validations.validateInsertRequest(request); Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { - Assert.assertEquals(ErrorMessage.EmptyKeyInTokens.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.EmptyKeyInTokens.getMessage(), e.getMessage()); } } @@ -612,7 +613,7 @@ public void testValidateInsertRequest_blankKeyInTokensThrows() { Validations.validateInsertRequest(request); Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { - Assert.assertEquals(ErrorMessage.EmptyKeyInTokens.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.EmptyKeyInTokens.getMessage(), e.getMessage()); } } @@ -1610,7 +1611,7 @@ public void testValidateDeleteRequest_nullRequestThrows() { Validations.validateDeleteRequest(null); Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { - Assert.assertEquals(ErrorMessage.DeleteRequestNull.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.DeleteRequestNull.getMessage(), e.getMessage()); } } @@ -1621,7 +1622,7 @@ public void testValidateDeleteRequest_nullTableThrows() { Validations.validateDeleteRequest(request); Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { - Assert.assertEquals(ErrorMessage.TableKeyError.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.TableKeyError.getMessage(), e.getMessage()); } } @@ -1632,7 +1633,7 @@ public void testValidateDeleteRequest_blankTableThrows() { Validations.validateDeleteRequest(request); Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { - Assert.assertEquals(ErrorMessage.EmptyTable.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.EmptyTable.getMessage(), e.getMessage()); } } @@ -1671,7 +1672,7 @@ public void testValidateDeleteRequest_blankIdInIdsThrows() { Validations.validateDeleteRequest(request); Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { - Assert.assertEquals(ErrorMessage.EmptyIdInIds.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.EmptyIdInIds.getMessage(), e.getMessage()); } } @@ -1722,7 +1723,7 @@ public void testValidateUpdateRequest_nullRequestThrows() { Validations.validateUpdateRequest(null); Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { - Assert.assertEquals(ErrorMessage.UpdateRequestNull.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.UpdateRequestNull.getMessage(), e.getMessage()); } } @@ -1734,7 +1735,7 @@ public void testValidateUpdateRequest_nullTableNameThrows() { Validations.validateUpdateRequest(request); Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { - Assert.assertEquals(ErrorMessage.TableKeyError.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.TableKeyError.getMessage(), e.getMessage()); } } @@ -1746,7 +1747,7 @@ public void testValidateUpdateRequest_blankTableNameThrows() { Validations.validateUpdateRequest(request); Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { - Assert.assertEquals(ErrorMessage.EmptyTable.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.EmptyTable.getMessage(), e.getMessage()); } } @@ -1845,7 +1846,7 @@ public void testValidateUpdateRequest_emptyTokensMapThrows() { Validations.validateUpdateRequest(request); Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { - Assert.assertEquals(ErrorMessage.EmptyTokens.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.EmptyTokens.getMessage(), e.getMessage()); } } @@ -1859,7 +1860,7 @@ public void testValidateUpdateRequest_blankKeyInTokensThrows() { Validations.validateUpdateRequest(request); Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { - Assert.assertEquals(ErrorMessage.EmptyKeyInTokens.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.EmptyKeyInTokens.getMessage(), e.getMessage()); } } @@ -1917,7 +1918,7 @@ public void testValidateGetRequest_nullRequestThrows() { Validations.validateGetRequest(null); Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { - Assert.assertEquals(ErrorMessage.GetRequestNull.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.GetRequestNull.getMessage(), e.getMessage()); } } @@ -1957,7 +1958,7 @@ public void testValidateGetRequest_nullTableThrows() { Validations.validateGetRequest(request); Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { - Assert.assertEquals(ErrorMessage.TableKeyError.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.TableKeyError.getMessage(), e.getMessage()); } } @@ -1968,7 +1969,7 @@ public void testValidateGetRequest_blankTableThrows() { Validations.validateGetRequest(request); Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { - Assert.assertEquals(ErrorMessage.EmptyTable.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.EmptyTable.getMessage(), e.getMessage()); } } @@ -2010,7 +2011,7 @@ public void testValidateGetRequest_blankIdInIdsThrows() { Validations.validateGetRequest(request); Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { - Assert.assertEquals(ErrorMessage.EmptyIdInIds.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.EmptyIdInIds.getMessage(), e.getMessage()); } } @@ -2039,7 +2040,7 @@ public void testValidateGetRequest_emptyFieldsThrows() { Validations.validateGetRequest(request); Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { - Assert.assertEquals(ErrorMessage.EmptyFields.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.EmptyFields.getMessage(), e.getMessage()); } } @@ -2054,7 +2055,7 @@ public void testValidateGetRequest_blankFieldInFieldsThrows() { Validations.validateGetRequest(request); Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { - Assert.assertEquals(ErrorMessage.EmptyFieldInFields.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.EmptyFieldInFields.getMessage(), e.getMessage()); } } @@ -2163,7 +2164,7 @@ public void testValidateGetRequest_multiTableRecordMissingTableThrows() { Validations.validateGetRequest(request); Assert.fail(EXCEPTION_NOT_THROWN); } catch (SkyflowException e) { - Assert.assertEquals(ErrorMessage.TableKeyError.getMessage(), e.getMessage()); + Assert.assertEquals(BaseErrorMessage.TableKeyError.getMessage(), e.getMessage()); } } } diff --git a/flowvault/src/test/java/com/skyflow/vault/controller/DetectControllerTests.java b/flowvault/src/test/java/com/skyflow/vault/controller/DetectControllerTests.java new file mode 100644 index 00000000..f21fe9f3 --- /dev/null +++ b/flowvault/src/test/java/com/skyflow/vault/controller/DetectControllerTests.java @@ -0,0 +1,634 @@ +package com.skyflow.vault.controller; + +import com.skyflow.Skyflow; +import com.skyflow.config.Credentials; +import com.skyflow.config.VaultConfig; +import com.skyflow.detect.CheckGuardrailsRequest; +import com.skyflow.detect.CheckGuardrailsResponse; +import com.skyflow.detect.DataSourceType; +import com.skyflow.detect.DeidentificationType; +import com.skyflow.detect.DeidentifyFileRequest; +import com.skyflow.detect.DeidentifyFileResponse; +import com.skyflow.detect.DeidentifyStringRequest; +import com.skyflow.detect.DeidentifyStringResponse; +import com.skyflow.detect.Detect; +import com.skyflow.detect.DetectConfiguration; +import com.skyflow.detect.DetectRunStatus; +import com.skyflow.detect.Entity; +import com.skyflow.detect.EntityType; +import com.skyflow.detect.FileDataFormat; +import com.skyflow.detect.GetRunRequest; +import com.skyflow.detect.GetRunResponse; +import com.skyflow.detect.GuardrailsValidation; +import com.skyflow.detect.PollOptions; +import com.skyflow.detect.RedactionLevel; +import com.skyflow.detect.ReidentifyFileRequest; +import com.skyflow.detect.ReidentifyFileResponse; +import com.skyflow.detect.ReidentifyStringRequest; +import com.skyflow.detect.ReidentifyStringResponse; +import com.skyflow.enums.Env; +import com.skyflow.errors.BaseErrorMessage; +import com.skyflow.errors.ErrorMessage; +import com.skyflow.errors.ErrorCode; +import com.skyflow.errors.SkyflowException; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import okhttp3.mockwebserver.RecordedRequest; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.File; +import java.nio.file.Files; +import java.util.Base64; + +import java.util.Arrays; +import java.util.Collections; + +public class DetectControllerTests { + + @Rule + public TemporaryFolder folder = new TemporaryFolder(); + + private static VaultConfig vaultConfig(String vaultId) { + VaultConfig config = new VaultConfig(); + config.setVaultId(vaultId); + config.setClusterId("cluster123"); + config.setEnv(Env.PROD); + return config; + } + + @Test + public void detectAccessorResolvesConfiguredVaults() throws SkyflowException { + Skyflow client = Skyflow.builder() + .addVaultConfig(vaultConfig("vault-a")) + .addVaultConfig(vaultConfig("vault-b")) + .build(); + Assert.assertNotNull(client.detect()); + Assert.assertSame(client.detect(), client.detect("vault-a")); + Assert.assertNotSame(client.detect("vault-a"), client.detect("vault-b")); + try { + client.detect("vault-c"); + Assert.fail("expected SkyflowException"); + } catch (SkyflowException e) { + Assert.assertEquals(BaseErrorMessage.VaultIdNotInConfigList.getMessage(), e.getMessage()); + } + } + + @Test + public void detectAccessorFollowsVaultConfigRemoval() throws SkyflowException { + Skyflow client = Skyflow.builder().addVaultConfig(vaultConfig("vault-a")).build(); + client.removeVaultConfig("vault-a"); + try { + client.detect(); + Assert.fail("expected SkyflowException"); + } catch (SkyflowException e) { + Assert.assertEquals(BaseErrorMessage.VaultIdNotInConfigList.getMessage(), e.getMessage()); + } + } + + @Test + public void deidentifyStringValidatesBeforeResolvingCredentials() throws SkyflowException { + DetectController controller = new DetectController(vaultConfig("vault-a"), (Credentials) null); + try { + controller.deidentifyString(DeidentifyStringRequest.builder().text(" ").configurationId("cfg").build()); + Assert.fail("expected SkyflowException"); + } catch (SkyflowException e) { + Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); + Assert.assertEquals(BaseErrorMessage.InvalidTextInDeidentifyString.getMessage(), e.getMessage()); + } + } + + @Test + public void reidentifyStringValidatesBeforeResolvingCredentials() throws SkyflowException { + DetectController controller = new DetectController(vaultConfig("vault-a"), (Credentials) null); + try { + controller.reidentifyString(ReidentifyStringRequest.builder().text(" ").build()); + Assert.fail("expected SkyflowException"); + } catch (SkyflowException e) { + Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); + Assert.assertEquals(BaseErrorMessage.InvalidTextInReidentifyString.getMessage(), e.getMessage()); + } + } + + /** + * Null credentials would make the SDK fall back to SKYFLOW_CREDENTIALS from the environment or a + * .env file, which CI provides, so the outcome would depend on the machine. An explicitly empty + * {@link Credentials} is rejected by validation the same way everywhere, before any network call. + */ + @Test + public void missingCredentialsSurfaceAsSkyflowExceptionWithoutNetworkCall() throws SkyflowException { + DetectController controller = new DetectController(vaultConfig("vault-a"), emptyCredentials()); + try { + controller.reidentifyString(ReidentifyStringRequest.builder().text("[NAME_1]").build()); + Assert.fail("expected SkyflowException"); + } catch (SkyflowException e) { + Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); + Assert.assertEquals(BaseErrorMessage.NoTokenGenerationMeansPassed.getMessage(), e.getMessage()); + } + } + + /** Credentials with no path, string, token or API key set. */ + private static Credentials emptyCredentials() { + return new Credentials(); + } + + // ─── reidentifyFile (flowvault only) ────────────────────────────────────── + + /** API-key credentials resolve without a token exchange, so the only network call is the Detect request. */ + private static Credentials apiKeyCredentials() { + Credentials credentials = new Credentials(); + credentials.setApiKey("sky-abcde-0123456789abcdef0123456789abcdef"); + return credentials; + } + + private static DetectController controllerFor(MockWebServer server) { + VaultConfig config = vaultConfig("vault-1"); + config.setVaultUrl(server.url("").toString()); + DetectController controller = new DetectController(config, apiKeyCredentials()); + controller.setCommonHttpConfig(null, 0, null, null); + return controller; + } + + @Test + public void reidentifyFileValidatesBeforeResolvingCredentials() throws SkyflowException { + DetectController controller = new DetectController(vaultConfig("vault-a"), (Credentials) null); + ReidentifyFileRequest[] bad = { + null, + ReidentifyFileRequest.builder().value("Zm9v").build(), + ReidentifyFileRequest.builder().dataSource(DataSourceType.BASE64).value(" ").build(), + }; + ErrorMessage[] expected = { + ErrorMessage.ReidentifyFileRequestNull, + ErrorMessage.InvalidDataSourceInReidentifyFile, + ErrorMessage.InvalidValueInReidentifyFile, + }; + for (int i = 0; i < bad.length; i++) { + try { + controller.reidentifyFile(bad[i]); + Assert.fail("expected SkyflowException for case " + i); + } catch (SkyflowException e) { + Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); + Assert.assertEquals(expected[i].getMessage(), e.getMessage()); + } + } + } + + @Test + public void reidentifyFileMissingCredentialsSurfaceWithoutNetworkCall() throws SkyflowException { + DetectController controller = new DetectController(vaultConfig("vault-a"), emptyCredentials()); + try { + controller.reidentifyFile(ReidentifyFileRequest.builder() + .dataSource(DataSourceType.BASE64).value("Zm9v").build()); + Assert.fail("expected SkyflowException"); + } catch (SkyflowException e) { + Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); + Assert.assertEquals(BaseErrorMessage.NoTokenGenerationMeansPassed.getMessage(), e.getMessage()); + } + } + + @Test + public void reidentifyFileSendsScopedRequestAndMapsResponse() throws Exception { + MockWebServer server = new MockWebServer(); + server.start(); + try { + server.enqueue(new MockResponse().setResponseCode(200).setHeader("Content-Type", "application/json") + .setBody("{\"status\":\"SUCCESS\",\"outputType\":\"BASE64\"," + + "\"output\":[{\"processedFile\":\"SGVsbG8=\",\"processedFileType\":\"REIDENTIFIED_FILE\"," + + "\"processedFileExtension\":\"txt\"}]," + + "\"metrics\":{\"size\":0.5,\"wordCount\":3,\"characterCount\":12}}")); + + ReidentifyFileResponse response = controllerFor(server).reidentifyFile(ReidentifyFileRequest.builder() + .dataSource(DataSourceType.BASE64) + .value("W05BTUVfMV0=") + .dataFormat(FileDataFormat.TXT) + .redactionLevel(Arrays.asList( + RedactionLevel.builder().tokenGroupName("names").redactionPattern("mask").build())) + .build()); + + Assert.assertEquals(DetectRunStatus.SUCCESS, response.getStatus()); + Assert.assertEquals(DataSourceType.BASE64, response.getOutputType()); + Assert.assertEquals(1, response.getOutput().size()); + Assert.assertEquals("SGVsbG8=", response.getOutput().get(0).getProcessedFile()); + Assert.assertEquals("REIDENTIFIED_FILE", response.getOutput().get(0).getProcessedFileType()); + Assert.assertEquals(FileDataFormat.TXT, response.getOutput().get(0).getProcessedFileExtension()); + Assert.assertEquals(Integer.valueOf(3), response.getMetrics().getWordCount()); + + RecordedRequest recorded = server.takeRequest(); + Assert.assertEquals("POST", recorded.getMethod()); + Assert.assertEquals("/v2/detect/reidentify/file", recorded.getPath()); + Assert.assertEquals("Bearer sky-abcde-0123456789abcdef0123456789abcdef", recorded.getHeader("Authorization")); + Assert.assertNotNull(recorded.getHeader("sky-metadata")); + String body = recorded.getBody().readUtf8(); + Assert.assertTrue(body, body.contains("\"dataSource\":\"BASE64\"")); + Assert.assertTrue(body, body.contains("\"value\":\"W05BTUVfMV0=\"")); + Assert.assertTrue(body, body.contains("\"vaultId\":\"vault-1\"")); + Assert.assertTrue(body, body.contains("\"dataFormat\":\"txt\"")); + Assert.assertTrue(body, body.contains("\"tokenGroupName\":\"names\"")); + Assert.assertTrue(body, body.contains("\"redactionPattern\":\"mask\"")); + } finally { + server.shutdown(); + } + } + + @Test + public void reidentifyFileToleratesSparseAndUnknownResponseValues() throws Exception { + MockWebServer server = new MockWebServer(); + server.start(); + try { + server.enqueue(new MockResponse().setResponseCode(200).setBody("{\"status\":\"SOMETHING_NEW\"}")); + ReidentifyFileResponse response = controllerFor(server).reidentifyFile(ReidentifyFileRequest.builder() + .dataSource(DataSourceType.SKYFLOW_ID).value("rec-1").build()); + Assert.assertEquals(DetectRunStatus.UNKNOWN, response.getStatus()); + Assert.assertNull(response.getOutputType()); + Assert.assertTrue(response.getOutput().isEmpty()); + Assert.assertNull(response.getMetrics()); + String body = server.takeRequest().getBody().readUtf8(); + Assert.assertFalse(body, body.contains("dataFormat")); + Assert.assertFalse(body, body.contains("redactionLevel")); + } finally { + server.shutdown(); + } + } + + @Test + public void reidentifyFileMapsApiErrors() throws Exception { + MockWebServer server = new MockWebServer(); + server.start(); + try { + server.enqueue(new MockResponse().setResponseCode(400).setHeader("x-request-id", "req-9") + .setBody("{\"error\":{\"grpc_code\":3,\"http_code\":400,\"message\":\"unsupported format\"," + + "\"http_status\":\"Bad Request\",\"details\":[]}}")); + try { + controllerFor(server).reidentifyFile(ReidentifyFileRequest.builder() + .dataSource(DataSourceType.PRESIGNED_URL).value("https://example.com/f").build()); + Assert.fail("expected SkyflowException"); + } catch (SkyflowException e) { + Assert.assertEquals(400, e.getHttpCode()); + Assert.assertEquals("unsupported format", e.getMessage()); + Assert.assertEquals("req-9", e.getRequestId()); + Assert.assertEquals(Integer.valueOf(3), e.getGrpcCode()); + } + } finally { + server.shutdown(); + } + } + + // ─── inherited Detect V2 methods, exercised through the flowvault controller ── + // These prove the flowvault wiring (vault URL from VaultConfig, API-key credentials, + // flowvault SDK metrics header) end to end, not just the shared base logic. + + private static final String API_KEY = "sky-abcde-0123456789abcdef0123456789abcdef"; + + private static void assertCommonHeaders(RecordedRequest recorded, String method, String path) { + Assert.assertEquals(method, recorded.getMethod()); + Assert.assertEquals(path, recorded.getPath()); + Assert.assertEquals("Bearer " + API_KEY, recorded.getHeader("Authorization")); + String metrics = recorded.getHeader("sky-metadata"); + Assert.assertNotNull(metrics); + Assert.assertTrue(metrics, metrics.contains("skyflow-flowvault-java@")); + } + + @Test + public void deidentifyStringRoundTripWithStoredConfiguration() throws Exception { + MockWebServer server = new MockWebServer(); + server.start(); + try { + server.enqueue(new MockResponse().setResponseCode(200).setHeader("Content-Type", "application/json") + .setBody("{\"processedText\":\"My name is [NAME_1].\"," + + "\"entities\":[{\"token\":\"NAME_1\",\"value\":\"John Doe\",\"entityType\":\"NAME\"," + + "\"location\":{\"startIndex\":11,\"endIndex\":19,\"startIndexProcessed\":11,\"endIndexProcessed\":19}," + + "\"entityScores\":{\"NAME\":0.98}}]," + + "\"metrics\":{\"wordCount\":4,\"characterCount\":20}}")); + + DeidentifyStringResponse response = controllerFor(server).deidentifyString(DeidentifyStringRequest.builder() + .text("My name is John Doe.").configurationId("cfg-123").build()); + + Assert.assertEquals("My name is [NAME_1].", response.getProcessedText()); + Assert.assertEquals(1, response.getEntities().size()); + Assert.assertEquals("NAME_1", response.getEntities().get(0).getToken()); + Assert.assertEquals("John Doe", response.getEntities().get(0).getValue()); + Assert.assertEquals("NAME", response.getEntities().get(0).getEntityType()); + Assert.assertEquals(Integer.valueOf(11), response.getEntities().get(0).getLocation().getStartIndex()); + Assert.assertEquals(Integer.valueOf(19), response.getEntities().get(0).getLocation().getEndIndexProcessed()); + Assert.assertEquals(0.98, response.getEntities().get(0).getEntityScores().get("NAME"), 1e-9); + Assert.assertEquals(Integer.valueOf(4), response.getMetrics().getWordCount()); + + RecordedRequest recorded = server.takeRequest(); + assertCommonHeaders(recorded, "POST", "/v2/detect/deidentify/string"); + String body = recorded.getBody().readUtf8(); + Assert.assertTrue(body, body.contains("\"text\":\"My name is John Doe.\"")); + Assert.assertTrue(body, body.contains("\"configurationId\":\"cfg-123\"")); + Assert.assertFalse(body, body.contains("\"configuration\":")); + } finally { + server.shutdown(); + } + } + + @Test + public void deidentifyStringRoundTripWithInlineConfigurationScopedToVault() throws Exception { + MockWebServer server = new MockWebServer(); + server.start(); + try { + server.enqueue(new MockResponse().setResponseCode(200).setBody("{\"processedText\":\"[NAME_1]\"}")); + + DeidentifyStringResponse response = controllerFor(server).deidentifyString(DeidentifyStringRequest.builder() + .text("John") + .configuration(DetectConfiguration.builder() + .detect(Detect.builder().entities(Collections.singletonList( + Entity.builder().entityType(EntityType.NAME) + .deidentificationType(DeidentificationType.VAULT_TOKEN) + .destination("names").build())).build()) + .build()) + .build()); + + Assert.assertEquals("[NAME_1]", response.getProcessedText()); + Assert.assertTrue(response.getEntities().isEmpty()); + Assert.assertNull(response.getMetrics()); + + String body = server.takeRequest().getBody().readUtf8(); + Assert.assertTrue(body, body.contains("\"configuration\":{")); + Assert.assertTrue(body, body.contains("\"vaultId\":\"vault-1\"")); // injected by the controller + Assert.assertTrue(body, body.contains("\"entityType\":\"NAME\"")); + Assert.assertTrue(body, body.contains("\"deidentificationType\":\"VAULT_TOKEN\"")); + Assert.assertTrue(body, body.contains("\"destination\":\"names\"")); + Assert.assertFalse(body, body.contains("configurationId")); + } finally { + server.shutdown(); + } + } + + @Test + public void reidentifyStringRoundTrip() throws Exception { + MockWebServer server = new MockWebServer(); + server.start(); + try { + server.enqueue(new MockResponse().setResponseCode(200) + .setBody("{\"processedText\":\"My name is J**n D*e.\",\"metrics\":{\"wordCount\":5}}")); + + ReidentifyStringResponse response = controllerFor(server).reidentifyString(ReidentifyStringRequest.builder() + .text("My name is [NAME_1].") + .redactionLevel(Collections.singletonList( + RedactionLevel.builder().tokenGroupName("names").redactionPattern("mask").build())) + .build()); + + Assert.assertEquals("My name is J**n D*e.", response.getProcessedText()); + Assert.assertEquals(Integer.valueOf(5), response.getMetrics().getWordCount()); + + RecordedRequest recorded = server.takeRequest(); + assertCommonHeaders(recorded, "POST", "/v2/detect/reidentify/string"); + String body = recorded.getBody().readUtf8(); + Assert.assertTrue(body, body.contains("\"text\":\"My name is [NAME_1].\"")); + Assert.assertTrue(body, body.contains("\"vaultId\":\"vault-1\"")); + Assert.assertTrue(body, body.contains("\"tokenGroupName\":\"names\"")); + Assert.assertTrue(body, body.contains("\"redactionPattern\":\"mask\"")); + } finally { + server.shutdown(); + } + } + + @Test + public void getRunRoundTripQueriesByIdScopedToVault() throws Exception { + MockWebServer server = new MockWebServer(); + server.start(); + try { + server.enqueue(new MockResponse().setResponseCode(200) + .setBody("{\"status\":\"SUCCESS\",\"outputType\":\"BASE64\"," + + "\"output\":[{\"processedFile\":\"UERG\",\"processedFileType\":\"REDACTED_FILE\",\"processedFileExtension\":\"pdf\"}]," + + "\"metrics\":{\"pages\":3}}")); + server.enqueue(new MockResponse().setResponseCode(200) + .setBody("{\"status\":\"FAILED\",\"message\":\"unsupported file\"}")); + + DetectController controller = controllerFor(server); + GetRunResponse done = controller.getRun(GetRunRequest.builder().runId(" run-42 ").build()); + Assert.assertEquals("run-42", done.getRunId()); + Assert.assertEquals(DetectRunStatus.SUCCESS, done.getStatus()); + Assert.assertEquals(DataSourceType.BASE64, done.getOutputType()); + Assert.assertEquals("UERG", done.getOutput().get(0).getProcessedFile()); + Assert.assertEquals("REDACTED_FILE", done.getOutput().get(0).getProcessedFileType()); + Assert.assertEquals(FileDataFormat.PDF, done.getOutput().get(0).getProcessedFileExtension()); + Assert.assertEquals(Integer.valueOf(3), done.getMetrics().getPages()); + Assert.assertNull(done.getMessage()); + + RecordedRequest recorded = server.takeRequest(); + assertCommonHeaders(recorded, "GET", "/v2/detect/runs/run-42?vaultId=vault-1"); + Assert.assertEquals(0L, recorded.getBodySize()); + + GetRunResponse failed = controller.getRun(GetRunRequest.builder().runId("run-43").build()); + Assert.assertEquals(DetectRunStatus.FAILED, failed.getStatus()); + Assert.assertEquals("unsupported file", failed.getMessage()); + Assert.assertTrue(failed.getOutput().isEmpty()); + Assert.assertNull(failed.getOutputType()); + Assert.assertEquals("/v2/detect/runs/run-43?vaultId=vault-1", server.takeRequest().getPath()); + } finally { + server.shutdown(); + } + } + + @Test + public void checkGuardrailsRoundTrip() throws Exception { + MockWebServer server = new MockWebServer(); + server.start(); + try { + server.enqueue(new MockResponse().setResponseCode(200) + .setBody("{\"text\":\"tell me about politics\",\"validation\":\"FAILED\",\"toxic\":false,\"deniedTopic\":true}")); + server.enqueue(new MockResponse().setResponseCode(200) + .setBody("{\"text\":\"hello\",\"validation\":\"PASSED\"}")); + + DetectController controller = controllerFor(server); + CheckGuardrailsResponse blocked = controller.checkGuardrails(CheckGuardrailsRequest.builder() + .text("tell me about politics").checkToxicity(true).denyTopics(Arrays.asList("politics", "religion")).build()); + Assert.assertEquals("tell me about politics", blocked.getText()); + Assert.assertEquals(GuardrailsValidation.FAILED, blocked.getValidation()); + Assert.assertEquals(Boolean.FALSE, blocked.getToxic()); + Assert.assertEquals(Boolean.TRUE, blocked.getDeniedTopic()); + + RecordedRequest recorded = server.takeRequest(); + assertCommonHeaders(recorded, "POST", "/v2/detect/guardrails"); + String body = recorded.getBody().readUtf8(); + Assert.assertTrue(body, body.contains("\"text\":\"tell me about politics\"")); + Assert.assertTrue(body, body.contains("\"vaultId\":\"vault-1\"")); + Assert.assertTrue(body, body.contains("\"checkToxicity\":true")); + Assert.assertTrue(body, body.contains("\"denyTopics\":[\"politics\",\"religion\"]")); + + CheckGuardrailsResponse passed = controller.checkGuardrails(CheckGuardrailsRequest.builder().text("hello").build()); + Assert.assertEquals(GuardrailsValidation.PASSED, passed.getValidation()); + Assert.assertNull(passed.getToxic()); + Assert.assertNull(passed.getDeniedTopic()); + String minimal = server.takeRequest().getBody().readUtf8(); + Assert.assertFalse(minimal, minimal.contains("checkToxicity")); + Assert.assertFalse(minimal, minimal.contains("denyTopics")); + } finally { + server.shutdown(); + } + } + + @Test + public void apiErrorsOnInheritedMethodsMapToSkyflowException() throws Exception { + MockWebServer server = new MockWebServer(); + server.start(); + try { + server.enqueue(new MockResponse().setResponseCode(404).setHeader("x-request-id", "req-404") + .setBody("{\"error\":{\"grpc_code\":5,\"http_code\":404,\"message\":\"run not found\",\"http_status\":\"Not Found\",\"details\":[]}}")); + try { + controllerFor(server).getRun(GetRunRequest.builder().runId("missing").build()); + Assert.fail("expected SkyflowException"); + } catch (SkyflowException e) { + Assert.assertEquals(404, e.getHttpCode()); + Assert.assertEquals("run not found", e.getMessage()); + Assert.assertEquals("req-404", e.getRequestId()); + Assert.assertEquals(Integer.valueOf(5), e.getGrpcCode()); + } + } finally { + server.shutdown(); + } + } + + // ─── deidentifyFile (flowvault only) ────────────────────────────────────── + + @Test + public void deidentifyFileValidatesBeforeResolvingCredentials() throws SkyflowException { + DetectController controller = new DetectController(vaultConfig("vault-a"), (Credentials) null); + try { + controller.deidentifyFile(DeidentifyFileRequest.builder().value("Zm9v").build()); + Assert.fail("expected SkyflowException"); + } catch (SkyflowException e) { + Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); + Assert.assertEquals(BaseErrorMessage.InvalidDataSourceInDeidentifyFile.getMessage(), e.getMessage()); + } + } + + @Test + public void deidentifyFileSubmitOnlyReturnsRunIdWithoutPolling() throws Exception { + MockWebServer server = new MockWebServer(); + server.start(); + try { + server.enqueue(new MockResponse().setResponseCode(200).setBody("{\"runId\":\"run-42\"}")); + + DeidentifyFileResponse response = controllerFor(server).deidentifyFile(DeidentifyFileRequest.builder() + .dataSource(DataSourceType.SKYFLOW_ID).value("rec-1").dataFormat(FileDataFormat.PDF) + .configurationId("cfg-1").build()); + + Assert.assertEquals("run-42", response.getRunId()); + Assert.assertNull(response.getStatus()); + Assert.assertTrue(response.getOutput().isEmpty()); + + RecordedRequest recorded = server.takeRequest(); + assertCommonHeaders(recorded, "POST", "/v2/detect/deidentify/file"); + String body = recorded.getBody().readUtf8(); + Assert.assertTrue(body, body.contains("\"dataSource\":\"SKYFLOW_ID\"")); + Assert.assertTrue(body, body.contains("\"value\":\"rec-1\"")); + Assert.assertTrue(body, body.contains("\"dataFormat\":\"pdf\"")); + Assert.assertTrue(body, body.contains("\"configurationId\":\"cfg-1\"")); + Assert.assertEquals("only the submit call was made", 1, server.getRequestCount()); + } finally { + server.shutdown(); + } + } + + @Test + public void deidentifyFilePollsToSuccessAndWritesOutputsForLocalFile() throws Exception { + File input = folder.newFile("invoice.txt"); + Files.write(input.toPath(), "Pay John Doe".getBytes("UTF-8")); + File outDir = folder.newFolder("out"); + String redacted = Base64.getEncoder().encodeToString("Pay [NAME_1]".getBytes("UTF-8")); + + MockWebServer server = new MockWebServer(); + server.start(); + try { + server.enqueue(new MockResponse().setResponseCode(200).setBody("{\"runId\":\"run-7\"}")); + server.enqueue(new MockResponse().setResponseCode(200).setBody("{\"status\":\"QUEUED\"}")); + server.enqueue(new MockResponse().setResponseCode(200).setBody("{\"status\":\"SUCCESS\",\"outputType\":\"BASE64\"," + + "\"output\":[{\"processedFile\":\"" + redacted + "\",\"processedFileType\":\"REDACTED_FILE\",\"processedFileExtension\":\"txt\"}," + + "{\"processedFile\":\"W10=\",\"processedFileType\":\"ENTITIES\",\"processedFileExtension\":\"json\"}]," + + "\"metrics\":{\"wordCount\":3}}")); + + DeidentifyFileResponse response = controllerFor(server).deidentifyFile(DeidentifyFileRequest.builder() + .file(input).configurationId("cfg-1") + .pollOptions(PollOptions.builder().waitTime(1).build()) // one real 1 s sleep at most + .outputDirectory(outDir.getPath()) + .build()); + + Assert.assertEquals("run-7", response.getRunId()); + Assert.assertEquals(DetectRunStatus.SUCCESS, response.getStatus()); + Assert.assertEquals(DataSourceType.BASE64, response.getOutputType()); + Assert.assertEquals(2, response.getOutput().size()); + Assert.assertEquals(Integer.valueOf(3), response.getMetrics().getWordCount()); + Assert.assertEquals("Pay [NAME_1]", new String(Files.readAllBytes(new File(outDir, "processed-invoice.txt").toPath()), "UTF-8")); + Assert.assertEquals("[]", new String(Files.readAllBytes(new File(outDir, "processed-entities-invoice.json").toPath()), "UTF-8")); + + RecordedRequest submit = server.takeRequest(); + assertCommonHeaders(submit, "POST", "/v2/detect/deidentify/file"); + String body = submit.getBody().readUtf8(); + Assert.assertTrue(body, body.contains("\"dataSource\":\"BASE64\"")); + Assert.assertTrue(body, body.contains("\"value\":\"" + Base64.getEncoder().encodeToString("Pay John Doe".getBytes("UTF-8")) + "\"")); + Assert.assertTrue(body, body.contains("\"dataFormat\":\"txt\"")); + Assert.assertEquals("/v2/detect/runs/run-7?vaultId=vault-1", server.takeRequest().getPath()); + Assert.assertEquals("/v2/detect/runs/run-7?vaultId=vault-1", server.takeRequest().getPath()); + } finally { + server.shutdown(); + } + } + + @Test + public void deidentifyFileReturnsRunIdWithStatusWhenBudgetRunsOut() throws Exception { + MockWebServer server = new MockWebServer(); + server.start(); + try { + server.enqueue(new MockResponse().setResponseCode(200).setBody("{\"runId\":\"run-8\"}")); + server.enqueue(new MockResponse().setResponseCode(200).setBody("{\"status\":\"IN_PROGRESS\"}")); + + DeidentifyFileResponse response = controllerFor(server).deidentifyFile(DeidentifyFileRequest.builder() + .dataSource(DataSourceType.BASE64).value("Zm9v") + .pollOptions(PollOptions.builder().maxAttempts(1).build()).build()); + + Assert.assertEquals("run-8", response.getRunId()); + Assert.assertEquals(DetectRunStatus.IN_PROGRESS, response.getStatus()); + Assert.assertTrue(response.getOutput().isEmpty()); + Assert.assertEquals(2, server.getRequestCount()); + } finally { + server.shutdown(); + } + } + + @Test + public void deidentifyFileSurfacesFailedRunMessage() throws Exception { + MockWebServer server = new MockWebServer(); + server.start(); + try { + server.enqueue(new MockResponse().setResponseCode(200).setBody("{\"runId\":\"run-9\"}")); + server.enqueue(new MockResponse().setResponseCode(200).setBody("{\"status\":\"FAILED\",\"message\":\"unsupported file\"}")); + + DeidentifyFileResponse response = controllerFor(server).deidentifyFile(DeidentifyFileRequest.builder() + .dataSource(DataSourceType.BASE64).value("Zm9v").pollOptions(PollOptions.builder().build()).build()); + + Assert.assertEquals(DetectRunStatus.FAILED, response.getStatus()); + Assert.assertEquals("unsupported file", response.getMessage()); + Assert.assertEquals(2, server.getRequestCount()); + } finally { + server.shutdown(); + } + } + + @Test + public void deidentifyFileSubmitErrorMapsToSkyflowException() throws Exception { + MockWebServer server = new MockWebServer(); + server.start(); + try { + server.enqueue(new MockResponse().setResponseCode(400).setHeader("x-request-id", "req-1") + .setBody("{\"error\":{\"grpc_code\":3,\"http_code\":400,\"message\":\"configuration not found\",\"http_status\":\"Bad Request\",\"details\":[]}}")); + try { + controllerFor(server).deidentifyFile(DeidentifyFileRequest.builder() + .dataSource(DataSourceType.BASE64).value("Zm9v").configurationId("missing").build()); + Assert.fail("expected SkyflowException"); + } catch (SkyflowException e) { + Assert.assertEquals(400, e.getHttpCode()); + Assert.assertEquals("configuration not found", e.getMessage()); + Assert.assertEquals("req-1", e.getRequestId()); + } + } finally { + server.shutdown(); + } + } +} diff --git a/skyvault/pom.xml b/skyvault/pom.xml index ce14cc82..23cb42d5 100644 --- a/skyvault/pom.xml +++ b/skyvault/pom.xml @@ -57,26 +57,19 @@ - - - - - org.apache.maven.plugins - maven-shade-plugin - 3.6.0 - + shade-for-japicmp package diff --git a/skyvault/src/main/java/com/skyflow/Skyflow.java b/skyvault/src/main/java/com/skyflow/Skyflow.java index 940f5008..53692860 100644 --- a/skyvault/src/main/java/com/skyflow/Skyflow.java +++ b/skyvault/src/main/java/com/skyflow/Skyflow.java @@ -5,10 +5,13 @@ import com.skyflow.config.VaultConfig; import com.skyflow.enums.LogLevel; import com.skyflow.errors.ErrorCode; +import com.skyflow.errors.BaseErrorMessage; import com.skyflow.errors.ErrorMessage; import com.skyflow.errors.SkyflowException; import com.skyflow.logs.ErrorLogs; import com.skyflow.logs.InfoLogs; +import com.skyflow.utils.Constants; +import com.skyflow.utils.SdkVersion; import com.skyflow.utils.Utils; import com.skyflow.utils.logger.LogUtil; import com.skyflow.utils.validations.Validations; @@ -32,6 +35,8 @@ protected Skyflow self() { } public static SkyflowClientBuilder builder() { + // Keep common's BaseErrorMessage prefix in step with this SDK's own ErrorMessage prefix. + SdkVersion.setSdkPrefix(Constants.SDK_PREFIX); return new SkyflowClientBuilder(); } @@ -99,27 +104,27 @@ public Skyflow updateLogLevel(LogLevel logLevel) { } public VaultController vault() throws SkyflowException { - return resolveOrThrow(this.builder.vaultClientsMap, null, ErrorLogs.VAULT_CONFIG_DOES_NOT_EXIST, ErrorMessage.VaultIdNotInConfigList); + return resolveOrThrow(this.builder.vaultClientsMap, null, ErrorLogs.VAULT_CONFIG_DOES_NOT_EXIST, BaseErrorMessage.VaultIdNotInConfigList); } public VaultController vault(String vaultId) throws SkyflowException { - return resolveOrThrow(this.builder.vaultClientsMap, vaultId, ErrorLogs.VAULT_CONFIG_DOES_NOT_EXIST, ErrorMessage.VaultIdNotInConfigList); + return resolveOrThrow(this.builder.vaultClientsMap, vaultId, ErrorLogs.VAULT_CONFIG_DOES_NOT_EXIST, BaseErrorMessage.VaultIdNotInConfigList); } public ConnectionController connection() throws SkyflowException { - return resolveOrThrow(this.builder.connectionsMap, null, ErrorLogs.CONNECTION_CONFIG_DOES_NOT_EXIST, ErrorMessage.ConnectionIdNotInConfigList); + return resolveOrThrow(this.builder.connectionsMap, null, ErrorLogs.CONNECTION_CONFIG_DOES_NOT_EXIST, BaseErrorMessage.ConnectionIdNotInConfigList); } public ConnectionController connection(String connectionId) throws SkyflowException { - return resolveOrThrow(this.builder.connectionsMap, connectionId, ErrorLogs.CONNECTION_CONFIG_DOES_NOT_EXIST, ErrorMessage.ConnectionIdNotInConfigList); + return resolveOrThrow(this.builder.connectionsMap, connectionId, ErrorLogs.CONNECTION_CONFIG_DOES_NOT_EXIST, BaseErrorMessage.ConnectionIdNotInConfigList); } public DetectController detect() throws SkyflowException { - return resolveOrThrow(this.builder.detectClientsMap, null, ErrorLogs.VAULT_CONFIG_DOES_NOT_EXIST, ErrorMessage.VaultIdNotInConfigList); + return resolveOrThrow(this.builder.detectClientsMap, null, ErrorLogs.VAULT_CONFIG_DOES_NOT_EXIST, BaseErrorMessage.VaultIdNotInConfigList); } public DetectController detect(String vaultId) throws SkyflowException { - return resolveOrThrow(this.builder.detectClientsMap, vaultId, ErrorLogs.VAULT_CONFIG_DOES_NOT_EXIST, ErrorMessage.VaultIdNotInConfigList); + return resolveOrThrow(this.builder.detectClientsMap, vaultId, ErrorLogs.VAULT_CONFIG_DOES_NOT_EXIST, BaseErrorMessage.VaultIdNotInConfigList); } public static final class SkyflowClientBuilder extends BaseSkyflowClientBuilder {