Skip to content
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package co.worklytics.psoxy.gateway;

import java.util.List;
import java.util.NoSuchElementException;
import java.util.Optional;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;

import java.util.NoSuchElementException;
import java.util.Optional;

@Builder
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
public class CompositeSecretStore implements SecretStore { //open to feedback on these names;
Expand Down Expand Up @@ -39,4 +39,14 @@ public Optional<ConfigService.ConfigValueWithMetadata> getConfigPropertyWithMeta
public void putConfigProperty(ConfigProperty property, String value) {
preferred.putConfigProperty(property, value);
}

@Override
public List<ConfigService.ConfigValueVersion> getAvailableVersions(ConfigProperty property, int limit) {
// Try preferred first, fall back to fallback if preferred returns empty
List<ConfigService.ConfigValueVersion> versions = preferred.getAvailableVersions(property, limit);
if (versions.isEmpty()) {
return fallback.getAvailableVersions(property, limit);
}
return versions;
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
package co.worklytics.psoxy.gateway;

import com.google.common.util.concurrent.Uninterruptibles;
import java.io.Serializable;
import java.time.Instant;
import java.util.Optional;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.Value;
import lombok.extern.java.Log;

import java.io.Serializable;
import java.time.Duration;
import java.time.Instant;
import java.util.Optional;

public interface ConfigService {

Expand Down Expand Up @@ -106,4 +102,26 @@ public Optional<Instant> getLastModifiedDate() {
return Optional.ofNullable(lastModifiedDate);
}
}

/**
* Represents a versioned configuration value with metadata
*/
@Builder
@Value
class ConfigValueVersion implements Serializable {
/**
* The actual value of the configuration
*/
String value;

/**
* When this version was last modified/created
*/
Instant lastModifiedDate;

/**
* Version identifier (typically numeric, but represented as Integer for flexibility)
*/
String version;
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
package co.worklytics.psoxy.gateway;

import java.util.List;

public interface SecretStore extends WritableConfigService {


/**
* Get available versions of a configuration property, ordered by version DESC (most recent first).
*
* This allows retrieving previous versions of secrets, which can be useful for cases like
* OAuth token rotation where a recently-rotated token might not yet be fully propagated.
*
* @param property the configuration property to retrieve versions for
* @param limit maximum number of versions to return
* @return list of available versions, ordered by version DESC; empty list if not supported or no versions exist
*/
List<ConfigService.ConfigValueVersion> getAvailableVersions(ConfigProperty property, int limit);

}
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
package co.worklytics.psoxy.gateway.impl;

import co.worklytics.psoxy.gateway.ConfigService;
import co.worklytics.psoxy.gateway.SecretStore;
import co.worklytics.psoxy.gateway.WritableConfigService;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;

import java.time.Duration;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import co.worklytics.psoxy.gateway.ConfigService;
import co.worklytics.psoxy.gateway.SecretStore;
import co.worklytics.psoxy.gateway.WritableConfigService;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;


@RequiredArgsConstructor
Expand Down Expand Up @@ -95,4 +96,17 @@ public Optional<String> getConfigPropertyAsOptional(ConfigProperty property) {
}
}

@Override
public List<ConfigService.ConfigValueVersion> getAvailableVersions(ConfigProperty property, int limit) {
// Don't cache version lists, always delegate to underlying implementation
if (delegate instanceof SecretStore) {
return ((SecretStore) delegate).getAvailableVersions(property, limit);
}
return delegate.getConfigPropertyWithMetadata(property).map(value -> ConfigService.ConfigValueVersion.builder()
.value(value.getValue())
.lastModifiedDate(value.getLastModifiedDate().orElse(null))
.version(null)
.build()).stream().collect(Collectors.toList());
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package co.worklytics.psoxy.gateway.impl;

import java.util.List;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.stream.Collectors;
import co.worklytics.psoxy.gateway.ConfigService;
import co.worklytics.psoxy.gateway.SecretStore;
import co.worklytics.psoxy.gateway.WritableConfigService;
Expand All @@ -8,9 +12,6 @@
import lombok.NonNull;
import lombok.RequiredArgsConstructor;

import java.util.NoSuchElementException;
import java.util.Optional;

/**
* constructs a composite ConfigService built from two others
*
Expand Down Expand Up @@ -59,4 +60,34 @@ public void putConfigProperty(ConfigProperty property, String value) {
throw new UnsupportedOperationException("preferred ConfigService is not writable");
}
}

@Override
public List<ConfigService.ConfigValueVersion> getAvailableVersions(ConfigProperty property, int limit) {
List<ConfigService.ConfigValueVersion> versions;
// Try preferred first if it's a SecretStore, otherwise fall back
if (preferred instanceof SecretStore) {
versions = ((SecretStore) preferred).getAvailableVersions(property, limit);
} else {
versions = preferred.getConfigPropertyWithMetadata(property).map(value -> ConfigService.ConfigValueVersion.builder()
.value(value.getValue())
.lastModifiedDate(value.getLastModifiedDate().orElse(null))
.version(null)
.build()).stream().collect(Collectors.toList());
}

if (versions.isEmpty()) {
// Try fallback if it's a SecretStore
if (fallback instanceof SecretStore) {
versions = ((SecretStore) fallback).getAvailableVersions(property, limit);
} else {
versions = fallback.getConfigPropertyWithMetadata(property).map(value -> ConfigService.ConfigValueVersion.builder()
.value(value.getValue())
.lastModifiedDate(value.getLastModifiedDate().orElse(null))
.version(null)
.build()).stream().collect(Collectors.toList());
}
}

return versions;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
@Value
public class AccessTokenDto {
@JsonCreator
public AccessTokenDto(@JsonProperty("token") String token, @JsonProperty("expirationDate") long expirationDate) {
public AccessTokenDto(@JsonProperty("token") String token, @JsonProperty("expirationDate") Long expirationDate) {
this.token = token;
this.expirationDate = expirationDate;
}
Expand Down
Loading