use of com.graphql_java_generator.plugin.language.FieldTypeAST in project graphql-maven-plugin-project by graphql-java-generator.
the class GenerateCodeJsonSchemaPersonalization method applySchemaPersonalization.
/**
* This is the 'main' method for this class: it loads the schema personalization from the json user file, and update
* what the {@link GenerateCodeDocumentParser} has already loaded according to the user's needs.
*/
public void applySchemaPersonalization() {
try {
if (!(configuration instanceof GenerateCodeCommonConfiguration) || !((GenerateCodeCommonConfiguration) configuration).getMode().equals(PluginMode.server)) {
logger.debug("The plugin configuration is not in server mode: no schema personalization is to be applied");
} else {
// First step: we load the schema personalization
if (getSchemaPersonalization() != null) {
// Then, we apply what has been loaded from the json file
for (EntityPersonalization objectPers : schemaPersonalization.getEntityPersonalizations()) {
ObjectType objectType = findObjectTypeFromName(objectPers.getName());
// Should we add an annotation ?
if (objectPers.getAddAnnotation() != null) {
objectType.addAnnotation(objectPers.getAddAnnotation());
}
// Should we replace the annotation ?
if (objectPers.getReplaceAnnotation() != null) {
objectType.addAnnotation(objectPers.getReplaceAnnotation(), true);
}
// Let's add all new fields
for (com.graphql_java_generator.plugin.schema_personalization.Field field : objectPers.getNewFields()) {
// There must not be any field of that name in that object
if (checkIfFieldExists(objectType, field.getName())) {
throw new RuntimeException("The object " + objectType.getName() + " already has a field of name " + field.getName());
}
// Ok, we can add this new field
FieldImpl newField;
if (field.getList() != null && field.getList()) {
// The new field is a list
FieldTypeAST listItem = FieldTypeAST.builder().graphQLTypeSimpleName(field.getType()).build();
FieldTypeAST list = FieldTypeAST.builder().listDepth(1).listItemFieldTypeAST(listItem).mandatory(field.getMandatory()).build();
newField = FieldImpl.builder().documentParser(documentParser).name(field.getName()).owningType(objectType).fieldTypeAST(list).build();
} else {
// The new field is not a list
newField = FieldImpl.builder().documentParser(documentParser).name(field.getName()).id(field.getId()).owningType(objectType).fieldTypeAST(FieldTypeAST.builder().graphQLTypeSimpleName(field.getType()).mandatory(field.getMandatory()).build()).build();
}
if (field.getAddAnnotation() != null) {
newField.addAnnotation(field.getAddAnnotation());
}
if (field.getReplaceAnnotation() != null) {
// We replace the annotation, even if there was an addAnnotation in the json file
newField.addAnnotation(field.getReplaceAnnotation(), true);
}
objectType.getFields().add(newField);
}
// Let's add personalize existing fields
for (com.graphql_java_generator.plugin.schema_personalization.Field field : objectPers.getFieldPersonalizations()) {
// Ok, we can add the field to personalize. This will throw an exception if not found
FieldImpl existingField = (FieldImpl) findFieldFromName(objectType, field.getName());
existingField.setName(field.getName());
if (field.getList() != null && (field.getList() != (existingField.getFieldTypeAST().getListDepth() > 0))) {
// The list attribute changed
if (field.getList()) {
// It's now a list (and it wasn't before)
FieldTypeAST list = FieldTypeAST.builder().listDepth(1).listItemFieldTypeAST(existingField.getFieldTypeAST()).build();
existingField.setFieldTypeAST(list);
} else {
// It's no more a list
existingField.setFieldTypeAST(existingField.getFieldTypeAST().getListItemFieldTypeAST());
}
}
if (field.getType() != null) {
existingField.getFieldTypeAST().setGraphQLTypeSimpleName(field.getType());
}
if (field.getId() != null) {
existingField.setId(field.getId());
}
if (field.getMandatory() != null) {
existingField.getFieldTypeAST().setMandatory(field.getMandatory());
}
if (field.getAddAnnotation() != null) {
existingField.addAnnotation(field.getAddAnnotation());
}
if (field.getReplaceAnnotation() != null) {
// We replace the annotation, even if there was an addAnnotation in the json file
existingField.addAnnotation(field.getReplaceAnnotation(), true);
}
}
// for personalize existing fields
}
}
}
} catch (IOException | URISyntaxException e) {
throw new RuntimeException("Can't apply schema personalization, due to: " + e.getMessage(), e);
}
}
use of com.graphql_java_generator.plugin.language.FieldTypeAST in project graphql-maven-plugin-project by graphql-java-generator.
the class AddRelayConnections method addConnectionInterface.
/**
* Adds the <I>Connection</I> interfaces. The <I>Connection</I> is described in the
* <A HREF="https://dev.to/mikemarcacci/intermediate-interfaces-generic-utility-types-in-graphql-50e8">generic
* utility types</A>, that leads to allow that an interface implements an interface in the GraphQL specification.If
* a <I>Connection</I> interface already exists in the schema, it is checked to be compliant to this specification.
* If not, an exception is thrown.
*
* @throws RuntimeException
* Thrown if a <I>Connection</I> interface already exists, but is not compliant with the above
* description
*/
void addConnectionInterface() {
final String CONNECTION = "Connection";
boolean found = false;
for (InterfaceType i : documentParser.getInterfaceTypes()) {
if (CONNECTION.equals(i.getName())) {
// We've found it.
found = true;
// Let's check its properties
if (i.getMemberOfUnions().size() != 0) {
throw new RuntimeException("The " + CONNECTION + " interface already exists, but is not compliant with the specification (is is a member of unions)");
}
if (i.getRequestType() != null) {
throw new RuntimeException("The " + CONNECTION + " interface already exists, but is not compliant with the Relay specification (it should not be a query/mutation/subscription)");
}
if (i.isInputType()) {
throw new RuntimeException("The " + CONNECTION + " interface already exists, but is not compliant with the Relay specification (it should not be an input type)");
}
if (i.getFields().size() != 2) {
throw new RuntimeException("The " + CONNECTION + " interface already exists, but is not compliant with the specification (it should contain exactly two fields)");
}
// ///////////// The pageInfo field
int j = 0;
if (!"pageInfo".equals(i.getFields().get(j).getName())) {
throw new RuntimeException("The " + CONNECTION + " interface already exists, but is not compliant with the Relay specification (the first field should be the 'pageInfo' field)");
}
if (!"PageInfo".equals(i.getFields().get(j).getGraphQLTypeSimpleName())) {
throw new RuntimeException("The " + CONNECTION + " interface already exists, but is not compliant with the Relay specification (the 'pageInfo' field must be of the 'PageInfo' type)");
}
if (i.getFields().get(j).isId()) {
throw new RuntimeException("The " + CONNECTION + " interface already exists, but is not compliant with the Relay specification (the 'pageInfo' field may not be an identifier)");
}
if (i.getFields().get(j).getFieldTypeAST().getListDepth() > 0) {
throw new RuntimeException("The " + CONNECTION + " interface already exists, but is not compliant with the Relay specification (the 'pageInfo' field may not be a list)");
}
if (!i.getFields().get(j).getFieldTypeAST().isMandatory()) {
throw new RuntimeException("The " + CONNECTION + " interface already exists, but is not compliant with the Relay specification (the 'pageInfo' field must be mandatory)");
}
// ///////////// The edges field
j += 1;
if (!"edges".equals(i.getFields().get(j).getName())) {
throw new RuntimeException("The " + CONNECTION + " interface already exists, but is not compliant with the Relay specification (the first field should be the 'edges' field)");
}
if (!"Edge".equals(i.getFields().get(j).getGraphQLTypeSimpleName())) {
throw new RuntimeException("The " + CONNECTION + " interface already exists, but is not compliant with the Relay specification (the 'edges' field must be of the 'PageInfo' type)");
}
if (i.getFields().get(j).isId()) {
throw new RuntimeException("The " + CONNECTION + " interface already exists, but is not compliant with the Relay specification (the 'edges' field may not be an identifier)");
}
if (!(i.getFields().get(j).getFieldTypeAST().getListDepth() > 0)) {
throw new RuntimeException("The " + CONNECTION + " interface already exists, but is not compliant with the Relay specification (the 'edges' field must be a list)");
}
// Ok, this interface is compliant. We're done.
break;
}
}
if (!found) {
// The interface Connection has not been found. But this name can (and may not) be used for another type.
if (documentParser.getType(CONNECTION, false) != null) {
throw new RuntimeException("A " + CONNECTION + " type already exists. This prevents to create the " + CONNECTION + " interface, as described in this article: https://dev.to/mikemarcacci/intermediate-interfaces-generic-utility-types-in-graphql-50e8");
}
// We're in the standard case: the interface doesn't exist in the given schema(s). Let's define it.
InterfaceType i = new InterfaceType(CONNECTION, configuration, documentParser);
// Adding the id field toe the Node interface
FieldTypeAST item = FieldTypeAST.builder().graphQLTypeSimpleName("Edge").build();
FieldTypeAST list = FieldTypeAST.builder().listDepth(1).listItemFieldTypeAST(item).build();
FieldImpl edges = FieldImpl.builder().name("edges").owningType(i).documentParser(documentParser).fieldTypeAST(list).build();
FieldImpl pageInfo = FieldImpl.builder().name("pageInfo").owningType(i).documentParser(documentParser).fieldTypeAST(//
FieldTypeAST.builder().graphQLTypeSimpleName("PageInfo").mandatory(true).build()).build();
i.getFields().add(edges);
i.getFields().add(pageInfo);
documentParser.getInterfaceTypes().add(i);
documentParser.getTypes().put(CONNECTION, i);
}
}
use of com.graphql_java_generator.plugin.language.FieldTypeAST in project graphql-maven-plugin-project by graphql-java-generator.
the class AddRelayConnections method addEdgeConnectionAndApplyNodeInterface.
/**
* This method searches for all interfaces, then type fields that have been marked by the RelayConnection directive,
* then, for each type, say <I>Xxx</I>:
* <UL>
* <LI>Creates the XxxEdge type</LI>
* <LI>Creates the XxxConnection type</LI>
* <LI>Add the <I>Node</I> interface as implemented by the Xxx type</LI>
* <LI>Update each field that is marked with the <I>@RelayConnection</I> interface, to change its type by the
* TypeConnection type instead</I>
* </UL>
*/
void addEdgeConnectionAndApplyNodeInterface() {
// A first step is to control that the schema is correct:
// * Standard case: a type's field has the @RelayConnection directive, and this field doesn't come from an
// interface.
// * More complex case: an interface's field has the @RelayConnection directive. We'll have to loop into
// each interface or type that implements this interface, and check that the relevant has the @RelayConnection
// directive. If not, a warning is issued.
// * Erroneous case: an interface or a type's field has the @RelayConnection directive, this field comes from an
// implemented interface, and the relevant field's interface doesn't have this directive.
//
// To do this:
//
// Step 1: identify all fields that have been marked with the @RelayConnection directive
// Step 2: for each of these fields, whether it is owned by an object or an interface, check if it inherits from
// an interface field. If yes, add the check that the field in the implemented interface is also marked with the
// @RelayConnection directive. If no, raise an error.
// Step 3: for fields of an interface that is marked with the @RelayConnection directive, checks that the
// implemented fields (that this: field if the same name in types that implement this interface) are also marked
// with the @RelayConnection directive. If not, a warning is issued, but the field is still added to the list of
// fields that implements the @RelayConnection directive
// Step 4: Identify the list of types and interfaces for which the Edge and Connection and Node interface should
// be done.
// Step 5: Actually implement the edges, connections and mark these types/interfaces with the Node interface
// Step 6: Update every field that implements the RelayConnection and change its type by the relevant
// XxxConnection object. The list of field to update is: all fields marked by the @RelayConnection, or that
// implements a field that is marked by the directive (see step3). This @RelayConnection directive must also be
// removed from the final schema
List<Field> fields = new ArrayList<>();
int nbErrors = 0;
// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Step 1: identify all fields in types and interfaces that have been marked with the @RelayConnection directive
// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Stream.concat(documentParser.getObjectTypes().stream(), documentParser.getInterfaceTypes().stream()).forEach((type) -> {
for (Field f : type.getFields()) {
// Is this field marked by the @RelayConnection directive?
for (AppliedDirective d : f.getAppliedDirectives()) {
if (d.getDirective().getName().equals("RelayConnection")) {
// It must be a list
if (!(f.getFieldTypeAST().getListDepth() > 0)) {
throw new RuntimeException("The " + f.getOwningType().getName() + "." + f.getName() + " field has the @RelayConnection directive applied, but is not a list. The @RelayConnection directive may only be applied on lists.");
}
// InputType may not have relay connection fields
if (((ObjectType) f.getOwningType()).isInputType()) {
throw new RuntimeException("The " + f.getOwningType().getName() + "." + f.getName() + " field has the @RelayConnection directive applied. But input type may not have fields to which the @RelayConnection directive is applied.");
}
//
// Everything is Ok. Let's go
fields.add(f);
break;
}
}
// for(AppliedDirective)
}
// for(Field))
});
// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
for (Field f : fields) {
// the current looping field
for (Field fieldInheritedFrom : getFieldInheritedFrom(f)) {
// This field must be marked by the @RelayConnection directive. So/and it must exist in the fields list
if (!fields.contains(fieldInheritedFrom)) {
logger.error("The field " + f.getName() + " of the " + (f.getOwningType() instanceof InterfaceType ? "interface" : "type") + " " + f.getOwningType().getName() + " has the directive @RelayConnection applied. But it inherits from the interface " + fieldInheritedFrom.getOwningType().getName() + ", in which this field doesn't have the directive @RelayConnection applied");
nbErrors += 1;
}
}
// for(getFieldInheritedFrom())
}
if (nbErrors > 0) {
throw new RuntimeException(nbErrors + " error(s) was(were) found in this schema, linked with the @RelayConnection schema. Please check the logged errors.");
}
// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Step 3: for fields of an interface that is marked with the @RelayConnection directive, checks that the
// implemented fields (that this: field if the same name in types that implement this interface) are also marked
// with the @RelayConnection directive. If not, a warning is issued, but the field is still added to the list of
// fields that implements the @RelayConnection directive
// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
List<Field> fieldsToAdd = new ArrayList<>();
for (Field field : fields) {
if (field.getOwningType() instanceof InterfaceType) {
for (Field inheritedField : getInheritedFields(field)) {
boolean found = false;
for (AppliedDirective d : inheritedField.getAppliedDirectives()) {
if (d.getDirective().getName().equals("RelayConnection")) {
found = true;
break;
}
}
if (!found) {
// We've found an object's field, inherited from an interface in which it is marked by the
// @RelayConnection directive. But this object's field is not marked with this directive. It's
// strange, but is generally Ok. So we display a warning. And we add this field to the list of
// field that must implement the relay connection.
logger.warn("The field " + inheritedField.getOwningType().getName() + "." + inheritedField.getName() + " implements (directly or indirectly) the " + field.getOwningType().getName() + "." + field.getName() + " field, but does not have the @RelayConnection directive");
// As we may not update a list, while we're looping in it, we create another list, that we'll be
// added afterward.
fieldsToAdd.add(inheritedField);
}
// if (!found)
}
// for(getInheritedFields)
}
// if
}
// for(fields)
fields.addAll(fieldsToAdd);
// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Step 4: Identify the list of types and interfaces for which the Edge and Connection and Node interface should
// be done.
// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Set<String> connectionTypeNames = new HashSet<>();
for (Field f : fields) {
connectionTypeNames.add(f.getGraphQLTypeSimpleName());
}
// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
for (String typeName : connectionTypeNames) {
Type type = documentParser.getType(typeName);
addNodeInterfaceToType(type);
generateConnectionType(type);
generateEdgeType(type);
}
// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
for (Field f : fields) {
FieldTypeAST fieldTypeAST = FieldTypeAST.builder().graphQLTypeSimpleName(f.getGraphQLTypeSimpleName() + "Connection").mandatory(false).build();
((FieldImpl) f).setFieldTypeAST(fieldTypeAST);
}
}
use of com.graphql_java_generator.plugin.language.FieldTypeAST in project graphql-maven-plugin-project by graphql-java-generator.
the class AddRelayConnections method generateConnectionType.
void generateConnectionType(Type type) {
final String connectionTypeName = type.getName() + "Connection";
// The XxxEdge type should not already exist
Type xxxConnection = documentParser.getType(connectionTypeName, false);
if (xxxConnection != null) {
if (!(xxxConnection instanceof ObjectType)) {
// GraphQL interface
throw new RuntimeException("The " + connectionTypeName + " already exist in the provided GraphQL schema. But it is not an Object, nor an interface.");
}
// at least the edges and the pageInfo fields.
if (xxxConnection.getFields().size() < 2) {
throw new RuntimeException("The " + connectionTypeName + " already exist in the provided GraphQL schema. But it is not compliant with the Relay connection specification: it should have at least the edges and the pageInfo fields");
}
boolean edgesFound = false;
boolean pageInfoFound = false;
for (Field f : xxxConnection.getFields()) {
switch(f.getName()) {
case "edges":
if (f.getType().getName().equals(type.getName() + "Edge") && !f.getFieldTypeAST().isMandatory() && f.getFieldTypeAST().getListDepth() > 0) {
// This field is compliant to the Relay specification
edgesFound = true;
}
break;
case "pageInfo":
if (f.getType().getName().equals("PageInfo") && f.getFieldTypeAST().isMandatory() && !(f.getFieldTypeAST().getListDepth() > 0)) {
// This field is compliant to the Relay specification
pageInfoFound = true;
}
break;
}
}
// for
if (!edgesFound || !pageInfoFound) {
throw new RuntimeException("The " + connectionTypeName + " already exist in the provided GraphQL schema. But it is not compliant with the Relay connection specification: it must have at least these fields:" + " edged(not mandatory, list of the " + type.getName() + " type) and pageInfo (mandatory, PageInfo type) fields");
}
} else {
// Standard case: the XxxConnection type doesn't exist. Let's create it.
ObjectType xxxConnectionObject;
if (type instanceof InterfaceType) {
xxxConnectionObject = new InterfaceType(connectionTypeName, configuration, documentParser);
documentParser.getInterfaceTypes().add((InterfaceType) xxxConnectionObject);
} else {
xxxConnectionObject = new ObjectType(connectionTypeName, configuration, documentParser);
documentParser.getObjectTypes().add(xxxConnectionObject);
}
documentParser.getTypes().put(connectionTypeName, xxxConnectionObject);
FieldTypeAST item = FieldTypeAST.builder().graphQLTypeSimpleName(type.getName() + "Edge").build();
FieldTypeAST list = FieldTypeAST.builder().listDepth(1).listItemFieldTypeAST(item).build();
FieldImpl edges = FieldImpl.builder().name("edges").documentParser(documentParser).owningType(xxxConnectionObject).fieldTypeAST(list).build();
xxxConnectionObject.getFields().add(edges);
FieldImpl pageInfo = FieldImpl.builder().name("pageInfo").documentParser(documentParser).owningType(xxxConnectionObject).fieldTypeAST(//
FieldTypeAST.builder().graphQLTypeSimpleName("PageInfo").mandatory(true).build()).build();
xxxConnectionObject.getFields().add(pageInfo);
// The XxxConnection objects/interfaces will implement the Connection interface, once the Generic type are
// managed here.
// xxxConnectionObject.getImplementz().add("Connection");
}
}
use of com.graphql_java_generator.plugin.language.FieldTypeAST in project graphql-maven-plugin-project by graphql-java-generator.
the class DocumentParser method readFieldTypeAST.
FieldTypeAST readFieldTypeAST(Object fieldDef) {
if (fieldDef instanceof TypeName) {
TypeName typeName = (TypeName) fieldDef;
FieldTypeAST ret = new FieldTypeAST(typeName.getName());
ret.setListDepth(0);
return ret;
} else if (fieldDef instanceof ListType) {
// This node contains a list. Let's recurse one.
ListType node = (ListType) fieldDef;
FieldTypeAST listItemTypeAST = readFieldTypeAST(node.getType());
// We return a list of the read subnode.
FieldTypeAST fieldTypeAST = new FieldTypeAST();
fieldTypeAST.setListDepth(listItemTypeAST.getListDepth() + 1);
fieldTypeAST.setListItemFieldTypeAST(listItemTypeAST);
fieldTypeAST.setItemMandatory(node.getChildren().get(0) instanceof NonNullType);
return fieldTypeAST;
} else if (fieldDef instanceof NonNullType) {
// Let's recurse in the AST for this mandatory type
NonNullType subNode = (NonNullType) fieldDef;
FieldTypeAST fieldTypeAST = readFieldTypeAST(subNode.getType());
// The type is mandatory
fieldTypeAST.setMandatory(true);
return fieldTypeAST;
} else {
throw new RuntimeException("Non managed fieldDef: " + fieldDef.getClass().getName());
}
}
Aggregations