Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
public class ObjectField extends GenerationSourceField<FieldDefinition> {
private int firstDefault = 100;
private int lastDefault = 100;
private boolean hasForwardPagination, hasBackwardPagination, hasRequiredPaginationFields;
private boolean hasForwardPagination, hasBackwardPagination, hasRequiredPaginationFields, hasPaginationTotalCountField;
private final List<ArgumentField> arguments, nonReservedArguments;
private final ArgumentField orderField;
private final LinkedHashMap<String, ArgumentField> argumentsByName;
Expand Down Expand Up @@ -128,6 +128,15 @@ public boolean hasPagination() {
return hasForwardPagination() || hasBackwardPagination();
}

public boolean hasTotalCountFieldInReturnType() {
return hasPaginationTotalCountField;
}

public void setHasTotalCountFieldInReturnType(boolean hasTotalCountField) {
hasPaginationTotalCountField = hasTotalCountField;
}


@Override
public boolean hasMutationType() {
return mutationType != null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,12 @@ private CodeBlock callQueryBlock(ObjectField target, String objectToCall, String
.build();
return CodeBlock
.builder()
.add("return $L.$L($L", target.hasServiceReference() ? newServiceDataFetcherWithTransform() : newDataFetcher(), getFetcherMethodName(target, localObject), indentIfMultiline(innerCode))
.add("return $L.$L($L",
target.hasServiceReference()
? newServiceDataFetcherWithTransform()
: newDataFetcher(),
getFetcherMethodName(target, localObject),
indentIfMultiline(innerCode))
.addStatement(")")
.build();
}
Expand Down Expand Up @@ -177,8 +182,13 @@ private CodeBlock callQueryBlockInner(ObjectField target, String objectToCall, S
params.add(VAR_RESOLVER_KEYS);
}
params.addAll(parser.getMethodInputNames(false, false, true));
var countFunction = countFunction(objectToCall, method, params, target.hasServiceReference());
return CodeBlock.of(" $N,\n$L,\n$L$L", VAR_PAGE_SIZE, queryFunction, countFunction, transformWrap);

if (target.hasTotalCountFieldInReturnType()) {
var countFunction = countFunction(objectToCall, method, params, target.hasServiceReference());
return CodeBlock.of(" $N,\n$L,\n$L$L", VAR_PAGE_SIZE, queryFunction, countFunction, transformWrap);
} else {
return CodeBlock.of(" $N,\n$L$L", VAR_PAGE_SIZE, queryFunction, transformWrap);
Comment on lines +186 to +190
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (target.hasTotalCountFieldInReturnType()) {
var countFunction = countFunction(objectToCall, method, params, target.hasServiceReference());
return CodeBlock.of(" $N,\n$L,\n$L$L", VAR_PAGE_SIZE, queryFunction, countFunction, transformWrap);
} else {
return CodeBlock.of(" $N,\n$L$L", VAR_PAGE_SIZE, queryFunction, transformWrap);
var countFunction = CodeBlock.ofIf(target.hasTotalCountFieldInReturnType(), ",\n$L", () -> countFunction(objectToCall, method, params, target.hasServiceReference());
return CodeBlock.of(" $N,\n$L$L$L", VAR_PAGE_SIZE, queryFunction, countFunction, transformWrap);

}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ public List<MethodSpec> generateAll() {
.stream()
.filter(GenerationField::isGeneratedWithResolver)
.filter(ObjectField::hasRequiredPaginationFields)
.filter(ObjectField::hasTotalCountFieldInReturnType)
.filter(it -> !it.hasServiceReference())
.map(this::generate)
.filter(it -> !it.code().isEmpty())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@ public ProcessedSchema(TypeDefinitionRegistry typeRegistry) {

federationIsImported = LinkDirectiveProcessor.loadFederationImportedDefinitions(typeRegistry) != null;
federationEntitiesExist = queryType != null && queryType.hasField(FEDERATION_ENTITIES_FIELD.getName()) && isFederationImported();

markConnectionsWithTotalCountField();
}

private SchemaDefinition createSchemaDefinition() {
Expand Down Expand Up @@ -196,6 +198,21 @@ private SchemaDefinition createSchemaDefinition() {
return definitionBuilder.build();
}

/**
* Mark fields whose return type is a connection type that contains the {@code totalCount} field. This is used to
* determine whether to generate a count method or not through
* {@link no.sikt.graphitron.generators.db.FetchCountDBMethodGenerator}.
*/
private void markConnectionsWithTotalCountField() {
this.objects
.values()
.stream()
.flatMap(obj -> obj.getFields().stream())
.filter(this::isConnectionObject)
.filter(this::hasTotalCountField)
.forEach(field -> field.setHasTotalCountFieldInReturnType(true));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Er litt skeptisk til å gjøre dette mutabelt på denne måten, det burde være nok å sjekke dette på selve connection-typen, men usikker på om det blir mer eller mindre komplekst. hasTotalCountField-metoden lenger nede burde være nok.

}

public boolean nodeExists() {
return nodeExists;
}
Expand Down Expand Up @@ -829,6 +846,16 @@ public boolean federationEntitiesExist() {
return federationEntitiesExist;
}

/**
* @param field A field whose type is assumed to be a connection object present in {@link #connectionObjects}.
* @return Whether this field points to a connection object type that has a {@code totalCount} field.
*/
private boolean hasTotalCountField(FieldSpecification field) {
return connectionObjects
.get(field.getTypeName())
.hasField(GraphQLReservedName.CONNECTION_TOTAL_COUNT.getName());
}

/**
* @return All types which could potentially have tables.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ public enum SchemaComponent {
CUSTOMER_TABLE("basic/CustomerTable"),
CUSTOMER_INPUT_TABLE("basic/CustomerInputTable"),
CUSTOMER_CONNECTION_ONLY("basic/CustomerConnection", PAGE_INFO),
CUSTOMER_CONNECTION_WITH_NO_OPTIONALS_ONLY("basic/CustomerConnectionWithNoOptionals", PAGE_INFO),
CUSTOMER_CONNECTION(CUSTOMER_TABLE, CUSTOMER_CONNECTION_ONLY),
CUSTOMER_CONNECTION_WITH_NO_OPTIONALS(CUSTOMER_TABLE, CUSTOMER_CONNECTION_WITH_NO_OPTIONALS_ONLY),
CUSTOMER_CONNECTION_ORDER(CUSTOMER_CONNECTION_ONLY, ORDER),
CUSTOMER_UNION("basic/CustomerUnion", CUSTOMER_QUERY),
CUSTOMER_NODE_INPUT_TABLE("basic/CustomerNodeInputTable", CUSTOMER_NODE),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,4 +146,23 @@ void queryAfterService() {
"queryNormal() {return _iv_env -> {return"
);
}

@Test
@DisplayName("When optional field 'totalCount' is included in the schema for a field having a service and pagination, generate the count method")
void serviceWithPaginationAndAllOptionalFieldsIncluded() {
assertGeneratedContentMatches(
"operation/withPaginationAndAllOptionalFieldsIncluded",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Det burde være nok å sjekke at count metoden kalles slik som i ResolverPaginationTest.

CUSTOMER_CONNECTION
);
}


@Test
@DisplayName("When optional field 'totalCount' is not included in the schema for a field having a service and pagination, do not generate the count method")
void serviceWithPaginationAndNoOptionalFieldsIncluded() {
assertGeneratedContentMatches(
"operation/withPaginationAndNoOptionalFieldsIncluded",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Samme her, nok å sjekke at den ikke kommer med.

CUSTOMER_CONNECTION_WITH_NO_OPTIONALS
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import java.util.Set;

import static no.sikt.graphitron.common.configuration.ReferencedEntry.CONTEXT_CONDITION;
import static no.sikt.graphitron.common.configuration.SchemaComponent.CUSTOMER_CONNECTION;
import static no.sikt.graphitron.common.configuration.SchemaComponent.CUSTOMER_CONNECTION_WITH_NO_OPTIONALS;
import static no.sikt.graphitron.common.configuration.SchemaComponent.DUMMY_CONNECTION;
import static no.sikt.graphitron.common.configuration.SchemaComponent.SPLIT_QUERY_WRAPPER;

Expand Down Expand Up @@ -104,4 +106,34 @@ void withContextCondition() {
"countQueryForQuery(_iv_ctx, _cf_ctxField)"
);
}

@Test
@DisplayName("When optional field 'totalCount' is included in the schema, generate the count method")
void countMethodCalledWhenAllOptionalFieldsIncluded() {
assertGeneratedContentContains(
"operation/allOptionalFieldsIncluded",
Set.of(CUSTOMER_CONNECTION),
"""
.loadPaginated(
_iv_pageSize,
(_iv_ctx, _iv_selectionSet) -> QueryDBQueries.customersForQuery(_iv_ctx, _iv_pageSize, _mi_after, _iv_selectionSet),
(_iv_ctx, _iv_keys) -> QueryDBQueries.countCustomersForQuery(_iv_ctx)
"""
);
}

@Test
@DisplayName("When optional field 'totalCount' is not included in the schema, do not generate the count method")
void countMethodNotCalledWhenAllOptionalFieldsDisabled() {
assertGeneratedContentContains(
"operation/allOptionalFieldsExcluded",
Set.of(CUSTOMER_CONNECTION_WITH_NO_OPTIONALS),
"""
.loadPaginated(
_iv_pageSize,
(_iv_ctx, _iv_selectionSet) -> QueryDBQueries.customersForQuery(_iv_ctx, _iv_pageSize, _mi_after, _iv_selectionSet)
);
"""
);
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package no.sikt.graphitron.queries.fetch;

import no.sikt.graphitron.common.GeneratorTest;
import no.sikt.graphitron.common.configuration.SchemaComponent;
import no.sikt.graphitron.generators.abstractions.ClassGenerator;
import no.sikt.graphitron.reducedgenerators.MapOnlyFetchDBClassGenerator;
import no.sikt.graphitron.reducedgenerators.PaginationOnlyDBClassGenerator;
import no.sikt.graphql.schema.ProcessedSchema;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
Expand All @@ -20,43 +19,63 @@ protected String getSubpath() {
return "queries/fetch/pagination";
}

@Override
protected Set<SchemaComponent> getComponents() {
return makeComponents(CUSTOMER_CONNECTION);
}

@Override
protected List<ClassGenerator> makeGenerators(ProcessedSchema schema) {
return List.of(new MapOnlyFetchDBClassGenerator(schema));
return List.of(new PaginationOnlyDBClassGenerator(schema));
}

@Test
@DisplayName("Connection with no other fields")
void defaultCase() {
assertGeneratedContentMatches("default");
assertGeneratedContentMatches("default", CUSTOMER_CONNECTION_WITH_NO_OPTIONALS);
}

@Test
@DisplayName("Connection with an extra field")
void withOtherField() {
assertGeneratedContentContains(
"withOtherField",
Set.of(CUSTOMER_CONNECTION_WITH_NO_OPTIONALS),
", String _mi_name, Integer _iv_pageSize, String _mi_after,"
);
}

@Test
@DisplayName("Connection not on the root level")
void splitQuery() {
assertGeneratedContentMatches("splitQuery", SPLIT_QUERY_WRAPPER);
assertGeneratedContentMatches(
"splitQuery",
CUSTOMER_CONNECTION_WITH_NO_OPTIONALS,
SPLIT_QUERY_WRAPPER
);
}

@Test
@DisplayName("No pagination on non-connection multiset")
void multiset() {
resultDoesNotContain("multiset", Set.of(CUSTOMER_TABLE),
resultDoesNotContain(
"multiset",
Set.of(CUSTOMER_TABLE),
".getOrderByValues(",
".seek("
);
}

@Test
@DisplayName("When optional field 'totalCount' is included in the schema, generate the count method")
void shouldGenerateAllOptionalFields() {
assertGeneratedContentContains(
"allOptionalFieldsIncluded",
Set.of(CUSTOMER_CONNECTION),
"countCustomersForQuery(");
}

@Test
@DisplayName("When optional field 'totalCount' is not included in the schema, do not generate the count method")
void shouldGenerateNoOptionalFields() {
resultDoesNotContain(
"allOptionalFieldsExcluded",
Set.of(CUSTOMER_CONNECTION_WITH_NO_OPTIONALS),
"countCustomersForQuery(");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package no.sikt.graphitron.reducedgenerators;

import no.sikt.graphitron.definitions.objects.ObjectDefinition;
import no.sikt.graphitron.generators.db.DBClassGenerator;
import no.sikt.graphitron.generators.db.FetchCountDBMethodGenerator;
import no.sikt.graphitron.generators.db.FetchMappedObjectDBMethodGenerator;
import no.sikt.graphitron.generators.db.SelectHelperDBMethodGenerator;
import no.sikt.graphitron.javapoet.TypeSpec;
import no.sikt.graphql.schema.ProcessedSchema;

import java.util.List;

public class PaginationOnlyDBClassGenerator extends DBClassGenerator {
public PaginationOnlyDBClassGenerator(ProcessedSchema processedSchema) {
super(processedSchema);
}

@Override
public TypeSpec generate(ObjectDefinition target) {
return getSpec(
target.getName(),
List.of(new FetchMappedObjectDBMethodGenerator(target, processedSchema),
new SelectHelperDBMethodGenerator(target, processedSchema),
new FetchCountDBMethodGenerator(target, processedSchema))
).build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ type CustomerConnection {
edges: [CustomerConnectionEdge]
pageInfo: PageInfo
nodes: [CustomerTable!]!
totalCount: Int
}

type CustomerConnectionEdge {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
type CustomerConnection {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gi den gjerne et annet navn så de ikke risikerer å kollidere med de andre. For eksempel CustomerNoOptionalsConnection osv.

Det eneste som har noe å si er at den slutter på Connection.

edges: [CustomerConnectionEdge]
pageInfo: PageInfo
}

type CustomerConnectionEdge {
cursor: String
node: CustomerTable
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ type DummyConnection {
edges: [DummyConnectionEdge]
pageInfo: PageInfo
nodes: [DummyType!]!
totalCount: Int
}

type DummyConnectionEdge {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
type PersonWithEmailConnection {
edges: [PersonWithEmailConnectionEdge]
nodes: [PersonWithEmail!]!
totalCount: Int
}

type PersonWithEmailConnectionEdge {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ type SomeUnionConnection {
edges: [SomeUnionConnectionEdge]
pageInfo: PageInfo
nodes: [SomeUnion!]!
totalCount: Int
}

type SomeUnionConnectionEdge {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
type Query {
customers(first: Int = 100, after: String): CustomerConnection
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
type Query {
customers(first: Int = 100, after: String): CustomerConnection
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import fake.code.generated.transform.RecordTransformer;
import fake.graphql.example.model.CustomerTable;
import graphql.schema.DataFetcher;
import java.lang.Integer;
import java.lang.String;
import java.util.concurrent.CompletableFuture;
import no.sikt.graphitron.codereferences.services.ResolverFetchService;
import no.sikt.graphql.helpers.resolvers.ResolverHelpers;
import no.sikt.graphql.helpers.resolvers.ServiceDataFetcherHelper;
import no.sikt.graphql.relay.ConnectionImpl;

public class QueryGeneratedDataFetcher {
public static DataFetcher<CompletableFuture<ConnectionImpl<CustomerTable>>> customers() {
return _iv_env -> {
Integer _mi_first = _iv_env.getArgument("first");
String _mi_after = _iv_env.getArgument("after");
int _iv_pageSize = ResolverHelpers.getPageSize(_mi_first, 1000, 100);
var _iv_transform = new RecordTransformer(_iv_env);
var _rs_resolverFetchService = new ResolverFetchService(_iv_transform.getCtx());
return new ServiceDataFetcherHelper<>(_iv_transform).loadPaginated(
_iv_pageSize,
() -> _rs_resolverFetchService.queryList(_iv_pageSize, _mi_after),
(_iv_keys) -> _rs_resolverFetchService.countQueryList(),
(_iv_recordTransform, _iv_response) -> _iv_recordTransform.customerTableRecordToGraphType(_iv_response, "")
);
} ;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
type Query {
customers(first: Int = 100, after: String): CustomerConnection @service(
service: {name: "RESOLVER_FETCH_SERVICE", method: "queryList"})
}
Loading