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
10 changes: 10 additions & 0 deletions commons/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ dependencies {
implementation group: 'org.ballerinalang', name: 'ballerina-lang', version: "${ballerinaLangVersion}"
implementation group: 'org.ballerinalang', name: 'ballerina-parser', version: "${ballerinaLangVersion}"
implementation group: 'org.ballerinalang', name: 'ballerina-tools-api', version: "${ballerinaLangVersion}"

testImplementation group: 'org.testng', name: 'testng', version: "${testngVersion}"
}

def excludePattern = '**/module-info.java'
Expand Down Expand Up @@ -89,6 +91,14 @@ spotbugsTest {
enabled = false
}

test {
useTestNG()
testLogging {
events "passed", "skipped", "failed"
showStandardStreams = false
}
}

compileJava {
doFirst {
options.compilerArgs = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,17 +174,61 @@ private boolean isFederatedDirective(Directive directive) {
}

private String getTypes() {
List<String> types = new ArrayList<>();
List<Type> typeList = new ArrayList<>();
for (Map.Entry<String, Type> entry : this.schema.getTypes().entrySet()) {
if (!isIntrospectionType(entry.getValue()) && !isBuiltInScalarType(entry.getValue())) {
if (!isSubgraphSdlIntrospection && isDefaultFederatedType(entry.getValue())) {
continue;
}
types.add(createType(entry.getValue()));
typeList.add(entry.getValue());
}
}

typeList.sort((t1, t2) -> {
int priority1 = getTypeSortPriority(t1);
int priority2 = getTypeSortPriority(t2);
if (priority1 != priority2) {
return Integer.compare(priority1, priority2);
}
return t1.getName().compareTo(t2.getName());
});

List<String> types = new ArrayList<>();
for (Type type : typeList) {
types.add(createType(type));
}
return String.join(LINE_SEPARATOR + LINE_SEPARATOR, types);
}

private int getTypeSortPriority(Type type) {
if (this.schema.getQueryType() != null && type.getName().equals(this.schema.getQueryType().getName())) {
return 0;
}
if (this.schema.getMutationType() != null && type.getName().equals(this.schema.getMutationType().getName())) {
return 1;
}
if (this.schema.getSubscriptionType() != null &&
type.getName().equals(this.schema.getSubscriptionType().getName())) {
return 2;
}

switch (type.getKind()) {
case INTERFACE:
return 3;
case OBJECT:
return 4;
case INPUT_OBJECT:
return 5;
case ENUM:
return 6;
case UNION:
return 7;
case SCALAR:
return 8;
default:
return 9;
}
}

private boolean isDefaultFederatedType(Type type) {
if (!isSubgraph) {
Expand Down
Copy link
Member

Choose a reason for hiding this comment

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

Good to see that you have added some tests here!

Copy link
Author

Choose a reason for hiding this comment

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

diff.patch
Here is a patch file showing it working in graphql tools

Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package io.ballerina.stdlib.graphql.commons.utils;

import io.ballerina.stdlib.graphql.commons.types.EnumValue;
import io.ballerina.stdlib.graphql.commons.types.Field;
import io.ballerina.stdlib.graphql.commons.types.InputValue;
import io.ballerina.stdlib.graphql.commons.types.Schema;
import io.ballerina.stdlib.graphql.commons.types.Type;
import io.ballerina.stdlib.graphql.commons.types.TypeKind;
import org.testng.Assert;
import org.testng.annotations.Test;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class SdlSchemaStringGeneratorTest {

@Test
public void testSchemaTypeOrdering() {
Schema schema = new Schema(null, false);

Type queryType = schema.addType("Query", TypeKind.OBJECT, null);
Type stringType = schema.addType("String", TypeKind.SCALAR, null);
queryType.addField(new Field("greeting", stringType));
queryType.addField(new Field("user", null));
schema.setQueryType(queryType);

Type mutationType = schema.addType("Mutation", TypeKind.OBJECT, null);
mutationType.addField(new Field("createUser", null));
schema.setMutationType(mutationType);

Type subscriptionType = schema.addType("Subscription", TypeKind.OBJECT, null);
subscriptionType.addField(new Field("userUpdated", null));
schema.setSubscriptionType(subscriptionType);

Type profileInterface = schema.addType("Profile", TypeKind.INTERFACE, null);
profileInterface.addField(new Field("id", stringType));
profileInterface.addField(new Field("name", stringType));

Type userType = schema.addType("User", TypeKind.OBJECT, null);
userType.addField(new Field("id", stringType));
userType.addField(new Field("name", stringType));
userType.addField(new Field("email", stringType));
userType.addInterface(profileInterface);

Type addressType = schema.addType("Address", TypeKind.OBJECT, null);
addressType.addField(new Field("street", stringType));
addressType.addField(new Field("city", stringType));

Type createUserInput = schema.addType("CreateUserInput", TypeKind.INPUT_OBJECT, null);
createUserInput.addInputField(new InputValue("name", stringType, null, null));
createUserInput.addInputField(new InputValue("email", stringType, null, null));

Type statusEnum = schema.addType("Status", TypeKind.ENUM, null);
statusEnum.addEnumValue(new EnumValue("ACTIVE", null));
statusEnum.addEnumValue(new EnumValue("INACTIVE", null));

Type searchResultUnion = schema.addType("SearchResult", TypeKind.UNION, null);
searchResultUnion.addPossibleType(userType);
searchResultUnion.addPossibleType(addressType);

Type dateTimeScalar = schema.addType("DateTime", TypeKind.SCALAR, null);

String sdlSchema = SdlSchemaStringGenerator.generate(schema);

int queryIndex = findTypeIndex(sdlSchema, "type Query");
int mutationIndex = findTypeIndex(sdlSchema, "type Mutation");
int subscriptionIndex = findTypeIndex(sdlSchema, "type Subscription");
int interfaceIndex = findTypeIndex(sdlSchema, "interface Profile");
int addressIndex = findTypeIndex(sdlSchema, "type Address");
int userIndex = findTypeIndex(sdlSchema, "type User");
int inputIndex = findTypeIndex(sdlSchema, "input CreateUserInput");
int enumIndex = findTypeIndex(sdlSchema, "enum Status");
int unionIndex = findTypeIndex(sdlSchema, "union SearchResult");
int scalarIndex = findTypeIndex(sdlSchema, "scalar DateTime");

Assert.assertTrue(queryIndex != -1, "Query type should be present");
Assert.assertTrue(mutationIndex != -1, "Mutation type should be present");
Assert.assertTrue(subscriptionIndex != -1, "Subscription type should be present");
Assert.assertTrue(interfaceIndex != -1, "Interface should be present");
Assert.assertTrue(userIndex != -1, "Object types should be present");
Assert.assertTrue(inputIndex != -1, "Input type should be present");
Assert.assertTrue(enumIndex != -1, "Enum type should be present");
Assert.assertTrue(unionIndex != -1, "Union type should be present");
Assert.assertTrue(scalarIndex != -1, "Scalar type should be present");

Assert.assertTrue(queryIndex < mutationIndex, "Query should come before Mutation");
Assert.assertTrue(mutationIndex < subscriptionIndex, "Mutation should come before Subscription");
Assert.assertTrue(subscriptionIndex < interfaceIndex, "Subscription should come before Interface");
Assert.assertTrue(interfaceIndex < addressIndex, "Interface should come before Object types");
Assert.assertTrue(interfaceIndex < userIndex, "Interface should come before Object types");
Assert.assertTrue(addressIndex < inputIndex, "Object types should come before Input");
Assert.assertTrue(userIndex < inputIndex, "Object types should come before Input");
Assert.assertTrue(inputIndex < enumIndex, "Input should come before Enum");
Assert.assertTrue(enumIndex < unionIndex, "Enum should come before Union");
Assert.assertTrue(unionIndex < scalarIndex, "Union should come before Scalar");
}

@Test
public void testAlphabeticalOrderingWithinSameTypeKind() {
Schema schema = new Schema(null, false);

Type queryType = schema.addType("Query", TypeKind.OBJECT, null);
Type stringType = schema.addType("String", TypeKind.SCALAR, null);
queryType.addField(new Field("test", stringType));
schema.setQueryType(queryType);

Type zebraType = schema.addType("Zebra", TypeKind.OBJECT, null);
zebraType.addField(new Field("name", stringType));

Type appleType = schema.addType("Apple", TypeKind.OBJECT, null);
appleType.addField(new Field("color", stringType));

Type mangoType = schema.addType("Mango", TypeKind.OBJECT, null);
mangoType.addField(new Field("taste", stringType));

String sdlSchema = SdlSchemaStringGenerator.generate(schema);

int appleIndex = findTypeIndex(sdlSchema, "type Apple");
int mangoIndex = findTypeIndex(sdlSchema, "type Mango");
int zebraIndex = findTypeIndex(sdlSchema, "type Zebra");

Assert.assertTrue(appleIndex < mangoIndex, "Apple should come before Mango (alphabetical order)");
Assert.assertTrue(mangoIndex < zebraIndex, "Mango should come before Zebra (alphabetical order)");
}

@Test
public void testMinimalSchemaWithOnlyQuery() {
Schema schema = new Schema(null, false);

Type queryType = schema.addType("Query", TypeKind.OBJECT, null);
Type stringType = schema.addType("String", TypeKind.SCALAR, null);
queryType.addField(new Field("hello", stringType));
schema.setQueryType(queryType);

String sdlSchema = SdlSchemaStringGenerator.generate(schema);

Assert.assertTrue(sdlSchema.contains("type Query"), "Schema should contain Query type");
Assert.assertTrue(sdlSchema.contains("hello"), "Query should have hello field");
Comment on lines +137 to +138
Copy link
Member

Choose a reason for hiding this comment

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

I think this test is not testing the exact functionality. Shall we add an end-to-end test where we generate the schema and then compare an existing schema defined using GraphQL SDL?

I think the schema generation tests are done inside the graphql-tools repository since the GraphQL tool is the place where we generate the tests. Can you please check that too? You can publish this to your local maven repository and use the SNAPSHOT version in the graphql tools to see the failing tests (this change must fail some tests there). Please let us know if you face any issues while doing this, can help you out.

@DimuthuMadushan am I missing anything here?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think you’ve covered everything. With this change, the SDL schema generation test needs to be updated.

}

private int findTypeIndex(String schema, String typePattern) {
Pattern pattern = Pattern.compile("^" + Pattern.quote(typePattern), Pattern.MULTILINE);
Matcher matcher = pattern.matcher(schema);
if (matcher.find()) {
return matcher.start();
}
return -1;
}
}

Loading