diff --git a/src/main/java/com/dtsx/astra/cli/core/models/DbRef.java b/src/main/java/com/dtsx/astra/cli/core/models/DbRef.java index b551156b..31a2d9d7 100644 --- a/src/main/java/com/dtsx/astra/cli/core/models/DbRef.java +++ b/src/main/java/com/dtsx/astra/cli/core/models/DbRef.java @@ -7,6 +7,7 @@ import lombok.*; import java.util.Map; +import java.util.Optional; import java.util.UUID; import java.util.function.Function; @@ -15,10 +16,17 @@ @EqualsAndHashCode @RequiredArgsConstructor(access = AccessLevel.PRIVATE) public class DbRef implements Highlightable { + private static final int UUID_LENGTH = 36; + private final Either ref; public static Either parse(@NonNull String ref) { return ModelUtils.trimAndValidateBasics("Database name/id", ref).flatMap((trimmed) -> { + val maybeIdFromEndpoint = tryParseEndpointUrl(trimmed); + if (maybeIdFromEndpoint.isPresent()) { + return Either.pure(new DbRef(Either.left(maybeIdFromEndpoint.get()))); + } + try { return Either.pure(new DbRef(Either.left(UUID.fromString(trimmed)))); } catch (IllegalArgumentException e) { @@ -27,6 +35,26 @@ public static Either parse(@NonNull String ref) { }); } + private static Optional tryParseEndpointUrl(String input) { + if (!input.startsWith("https://") && !input.startsWith("http://")) { + return Optional.empty(); + } + + val hostStart = input.indexOf("://") + 3; + val afterScheme = input.substring(hostStart); + val host = afterScheme.contains("/") ? afterScheme.substring(0, afterScheme.indexOf('/')) : afterScheme; + + if (host.length() <= UUID_LENGTH || host.charAt(UUID_LENGTH) != '-') { + return Optional.empty(); + } + + try { + return Optional.of(UUID.fromString(host.substring(0, UUID_LENGTH))); + } catch (IllegalArgumentException e) { + return Optional.empty(); + } + } + public static DbRef fromNameUnsafe(@NonNull String name) { return new DbRef(Either.pure(name)); } diff --git a/src/test/java/com/dtsx/astra/cli/unit/core/models/DbRefTest.java b/src/test/java/com/dtsx/astra/cli/unit/core/models/DbRefTest.java new file mode 100644 index 00000000..49bc6643 --- /dev/null +++ b/src/test/java/com/dtsx/astra/cli/unit/core/models/DbRefTest.java @@ -0,0 +1,88 @@ +package com.dtsx.astra.cli.unit.core.models; + +import com.dtsx.astra.cli.core.models.DbRef; +import com.dtsx.astra.cli.unit.BaseParseableTest; +import lombok.val; +import net.jqwik.api.Example; +import net.jqwik.api.Group; + +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; + +public class DbRefTest extends BaseParseableTest.WithTrimAndBasicValidation { + private static final UUID SAMPLE_ID = UUID.fromString("822b0fff-6a73-4322-a8ec-09832b075287"); + + public DbRefTest() { + super("Database name/id", DbRef::parse); + } + + @Group + public class from_name { + @Example + public void parses_plain_name_as_name_ref() { + val result = DbRef.parse("my-database"); + assertThat(result.getRight().isName()).isTrue(); + assertThat(result.getRight().toString()).isEqualTo("my-database"); + } + } + + @Group + public class from_uuid { + @Example + public void parses_uuid_as_id_ref() { + val result = DbRef.parse(SAMPLE_ID.toString()); + assertThat(result.getRight().isId()).isTrue(); + assertThat(result.getRight().toString()).isEqualTo(SAMPLE_ID.toString()); + } + } + + @Group + public class from_endpoint_url { + @Example + public void parses_prod_api_endpoint() { + val url = "https://" + SAMPLE_ID + "-us-east1.apps.astra.datastax.com"; + val result = DbRef.parse(url); + assertThat(result.getRight().isId()).isTrue(); + assertThat(result.getRight().toString()).isEqualTo(SAMPLE_ID.toString()); + } + + @Example + public void parses_dev_api_endpoint() { + val url = "https://" + SAMPLE_ID + "-ap-south-1.apps.astra-dev.datastax.com"; + val result = DbRef.parse(url); + assertThat(result.getRight().isId()).isTrue(); + assertThat(result.getRight().toString()).isEqualTo(SAMPLE_ID.toString()); + } + + @Example + public void parses_endpoint_with_path() { + val url = "https://" + SAMPLE_ID + "-us-east1.apps.astra.datastax.com/api/json/v1"; + val result = DbRef.parse(url); + assertThat(result.getRight().isId()).isTrue(); + assertThat(result.getRight().toString()).isEqualTo(SAMPLE_ID.toString()); + } + + @Example + public void parses_swagger_endpoint() { + val url = "https://" + SAMPLE_ID + "-ap-south-1.apps.astra-dev.datastax.com/api/json/swagger-ui"; + val result = DbRef.parse(url); + assertThat(result.getRight().isId()).isTrue(); + assertThat(result.getRight().toString()).isEqualTo(SAMPLE_ID.toString()); + } + + @Example + public void falls_back_to_name_for_non_astra_url() { + val url = "https://some-other-host.example.com/path"; + val result = DbRef.parse(url); + assertThat(result.getRight().isName()).isTrue(); + } + + @Example + public void falls_back_to_name_when_url_has_no_uuid_prefix() { + val url = "https://not-a-uuid-at-all.apps.astra.datastax.com"; + val result = DbRef.parse(url); + assertThat(result.getRight().isName()).isTrue(); + } + } +}