Skip to content

Commit

Permalink
Merge pull request #6 from fauna/BT-4347-query-building
Browse files Browse the repository at this point in the history
BT-4347 Query Building
  • Loading branch information
IvanAlfer authored Nov 3, 2023
2 parents 6fac875 + 9e1d85b commit fae4f08
Show file tree
Hide file tree
Showing 8 changed files with 334 additions and 23 deletions.
15 changes: 15 additions & 0 deletions faunaJava/src/main/java/com/fauna/query/builder/Fragment.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.fauna.query.builder;

/**
* An abstract class serving as a base for different types of query fragments.
*/
abstract class Fragment {

/**
* Retrieves the value represented by this fragment.
*
* @return the value of this fragment.
*/
public abstract Object get();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.fauna.query.builder;

import java.util.Objects;

/**
* Represents a literal fragment of a Fauna query.
* This class encapsulates a fixed string that does not contain any variables.
*/
class LiteralFragment extends Fragment {

private final String value;

/**
* Constructs a new {@code LiteralFragment} with the given literal value.
*
* @param value the string value of this fragment; must not be null.
* @throws IllegalArgumentException if {@code value} is null.
*/
LiteralFragment(String value) {
if (value == null) {
throw new IllegalArgumentException("A literal value must not be null");
}
this.value = value;
}

/**
* Retrieves the string value of this fragment.
*
* @return the string value.
*/
@Override
public String get() {
return value;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;

LiteralFragment that = (LiteralFragment) o;

return Objects.equals(value, that.value);
}

@Override
public int hashCode() {
return value != null ? value.hashCode() : 0;
}

}
73 changes: 73 additions & 0 deletions faunaJava/src/main/java/com/fauna/query/builder/Query.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.fauna.query.builder;

import com.fauna.query.template.FaunaTemplate;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
* Represents a Fauna query that is constructed from fragments.
* This class allows the building of queries from literal and variable parts.
*/
public class Query {

private final List<Fragment> fragments;

/**
* Constructs an empty Query instance.
*/
public Query() {
this.fragments = new ArrayList<>();
}

/**
* Adds a fragment to the query.
*
* @param fragment the Fragment to add to the query.
*/
void addFragment(Fragment fragment) {
fragments.add(fragment);
}

/**
* Creates a Query instance from a string template and arguments.
* The template string can contain literals and variable placeholders.
*
* @param query the string template of the query.
* @param args the arguments to replace the variable placeholders in the query.
* @return a Query instance representing the complete query.
* @throws IllegalArgumentException if a template variable does not have a corresponding entry in the provided args.
*/
public static Query fql(String query, Map<String, Object> args) throws IllegalArgumentException {
Query faunaQuery = new Query();
FaunaTemplate template = new FaunaTemplate(query);

for (FaunaTemplate.TemplatePart part : template) {
switch (part.getType()) {
case LITERAL: {
faunaQuery.addFragment(new LiteralFragment(part.getPart()));
break;
}
case VARIABLE: {
if (!args.containsKey(part.getPart())) {
throw new IllegalArgumentException("Template variable `" + part.getPart() + "` not found in provided args");
}
faunaQuery.addFragment(new ValueFragment(args.get(part.getPart())));
break;
}
}
}
return faunaQuery;
}

/**
* Retrieves the list of fragments that make up this query.
*
* @return a list of Fragments.
*/
List<Fragment> getFragments() {
return fragments;
}

}
47 changes: 47 additions & 0 deletions faunaJava/src/main/java/com/fauna/query/builder/ValueFragment.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.fauna.query.builder;

import java.util.Objects;

/**
* Represents a value fragment of a Fauna query.
* This class encapsulates a value that can be a variable in the query.
*/
class ValueFragment extends Fragment {

private final Object value;

/**
* Constructs a ValueFragment with the specified value.
*
* @param value the value to encapsulate, which can be any object.
*/
public ValueFragment(Object value) {
this.value = value;
}

/**
* Retrieves the encapsulated value of this fragment.
*
* @return the encapsulated object.
*/
@Override
public Object get() {
return value;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;

ValueFragment that = (ValueFragment) o;

return Objects.equals(value, that.value);
}

@Override
public int hashCode() {
return value != null ? value.hashCode() : 0;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* Represents a template for constructing Fauna queries with placeholders
* for variable interpolation.
*/
public class FaunaTemplate implements Iterable<FaunaTemplate.TemplatePart> {

private static final char DELIMITER = '$';
Expand All @@ -21,10 +25,21 @@ public class FaunaTemplate implements Iterable<FaunaTemplate.TemplatePart> {

private final String template;

/**
* Constructs a new FaunaTemplate with the given string template.
*
* @param template the string template containing literals and placeholders.
*/
public FaunaTemplate(String template) {
this.template = template;
}

/**
* Creates an iterator over the parts of the template, distinguishing
* between literals and variable placeholders.
*
* @return an Iterator that traverses the template parts.
*/
@Override
public Iterator<TemplatePart> iterator() {
return new Iterator<>() {
Expand Down Expand Up @@ -87,6 +102,12 @@ public TemplatePart next() {
};
}

/**
* Handles invalid placeholder syntax within the template.
*
* @param position the starting position of the invalid placeholder.
* @throws IllegalArgumentException if the placeholder syntax is invalid.
*/
private void handleInvalid(int position) {
String substringUpToPosition = template.substring(0, position);
String[] lines = substringUpToPosition.split("\r?\n");
Expand All @@ -103,19 +124,39 @@ private void handleInvalid(int position) {
throw new IllegalArgumentException(String.format("Invalid placeholder in template: line %d, col %d", lineno, colno));
}

/**
* Represents a part of the template, which can either be a literal string
* or a variable placeholder.
*/
public static class TemplatePart {
private final String part;
private final TemplatePartType type;

/**
* Constructs a new TemplatePart with the given text and type.
*
* @param part the text of this part of the template.
* @param type the type of this part of the template.
*/
public TemplatePart(String part, TemplatePartType type) {
this.part = part;
this.type = type;
}

/**
* Retrieves the text of this part of the template.
*
* @return the text of this template part.
*/
public String getPart() {
return part;
}

/**
* Retrieves the type of this part of the template.
*
* @return the type of this template part.
*/
public TemplatePartType getType() {
return type;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
package com.fauna.query.template;

/**
* Represents the type of template part within a FaunaTemplate.
*/
public enum TemplatePartType {

/**
* Indicates a literal text part of the template.
*/
LITERAL,

/**
* Indicates a variable placeholder part of the template.
*/
VARIABLE

}
23 changes: 0 additions & 23 deletions faunaJava/src/test/java/com/fauna/client/FaunaClientTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,14 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.net.http.HttpResponse;
import java.util.concurrent.CompletableFuture;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.when;

class FaunaClientTest {

@Mock
private Connection connection;

@Mock
private HttpResponse<String> httpResponse;

private FaunaClient faunaClient;

@BeforeEach
Expand All @@ -45,18 +36,4 @@ void query_WhenFqlIsNull_ShouldThrowIllegalArgumentException() {
assertTrue(thrown.getMessage().contains("The provided FQL query is null."));
}

@Test
void query_WhenFqlIsValid_ShouldReturnHttpResponse() {
String fql = "valid FQL query";
when(connection.performRequest(fql)).thenReturn(CompletableFuture.completedFuture(httpResponse));
when(httpResponse.statusCode()).thenReturn(200);
when(httpResponse.body()).thenReturn("{\"data\": {}}");

CompletableFuture<HttpResponse<String>> result = faunaClient.query(fql);

assertNotNull(result);
assertTrue(result.isDone());
assertEquals(httpResponse, result.join());
}

}
Loading

0 comments on commit fae4f08

Please sign in to comment.