未验证 提交 d8073a92 编写于 作者: B Bogdan Kobylynskyi 提交者: GitHub

Introduce codegen for client code - Part 1. #37 (#53)

* Introduce codegen for client code - Part 1. #37

* Remove redundant null-checks

* Minor fixes in Request class generation + improve code coverage. #37

* Customizable suffix of ResponseProjection classes. #37

* Code coverage for MappingConfig #37

* Fix method names #37 #53
上级 c1ee76d9
......@@ -12,9 +12,11 @@ class FreeMarkerTemplatesRegistry {
static Template typeTemplate;
static Template enumTemplate;
static Template unionTemplate;
static Template requestTemplate;
static Template interfaceTemplate;
static Template operationsTemplate;
static Template fieldsResolverTemplate;
static Template responseProjectionTemplate;
static {
Configuration configuration = new Configuration(Configuration.VERSION_2_3_28);
......@@ -28,9 +30,11 @@ class FreeMarkerTemplatesRegistry {
typeTemplate = configuration.getTemplate("templates/javaClassGraphqlType.ftl");
enumTemplate = configuration.getTemplate("templates/javaClassGraphqlEnum.ftl");
unionTemplate = configuration.getTemplate("templates/javaClassGraphqlUnion.ftl");
requestTemplate = configuration.getTemplate("templates/javaClassGraphqlRequest.ftl");
interfaceTemplate = configuration.getTemplate("templates/javaClassGraphqlInterface.ftl");
operationsTemplate = configuration.getTemplate("templates/javaClassGraphqlOperations.ftl");
fieldsResolverTemplate = configuration.getTemplate("templates/javaClassGraphqlFieldsResolver.ftl");
responseProjectionTemplate = configuration.getTemplate("templates/javaClassGraphqlResponseProjection.ftl");
} catch (IOException e) {
throw new UnableToLoadFreeMarkerTemplateException(e);
}
......
package com.kobylynskyi.graphql.codegen;
import com.kobylynskyi.graphql.codegen.mapper.*;
import com.kobylynskyi.graphql.codegen.model.*;
import com.kobylynskyi.graphql.codegen.model.DefaultMappingConfigValues;
import com.kobylynskyi.graphql.codegen.model.DefinitionTypeDeterminer;
import com.kobylynskyi.graphql.codegen.model.MappingConfig;
import com.kobylynskyi.graphql.codegen.model.UnsupportedGraphqlDefinitionException;
import com.kobylynskyi.graphql.codegen.supplier.MappingConfigSupplier;
import com.kobylynskyi.graphql.codegen.utils.Utils;
import freemarker.template.TemplateException;
import graphql.language.*;
import lombok.Getter;
......@@ -12,17 +16,16 @@ import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import static java.util.stream.Collectors.toList;
/**
* Generator of:
* - Interface for each GraphQL query
* - Interface for each GraphQL mutation
* - Interface for each GraphQL subscription
* - Class for each GraphQL data type
* - Class for each GraphQL enum type
* - Class for each GraphQL scalar type
* - Interface for each GraphQL query, mutation, subscription, union and field resolvers
* - POJO Class for each GraphQL type and input
* - Enum Class for each GraphQL enum
*
* @author kobylynskyi
* @author valinhadev
......@@ -55,6 +58,15 @@ public class GraphqlCodegen {
if (mappingConfig.getGenerateEqualsAndHashCode() == null) {
mappingConfig.setGenerateEqualsAndHashCode(DefaultMappingConfigValues.DEFAULT_EQUALS_AND_HASHCODE);
}
if (mappingConfig.getGenerateRequests() == null) {
mappingConfig.setGenerateRequests(DefaultMappingConfigValues.DEFAULT_GENERATE_REQUESTS);
}
if (mappingConfig.getRequestSuffix() == null) {
mappingConfig.setRequestSuffix(DefaultMappingConfigValues.DEFAULT_REQUEST_SUFFIX);
}
if (mappingConfig.getResponseProjectionSuffix() == null) {
mappingConfig.setResponseProjectionSuffix(DefaultMappingConfigValues.DEFAULT_RESPONSE_PROJECTION_SUFFIX);
}
if (mappingConfig.getGenerateToString() == null) {
mappingConfig.setGenerateToString(DefaultMappingConfigValues.DEFAULT_TO_STRING);
}
......@@ -64,6 +76,10 @@ public class GraphqlCodegen {
if (mappingConfig.getGenerateParameterizedFieldsResolvers() == null) {
mappingConfig.setGenerateParameterizedFieldsResolvers(DefaultMappingConfigValues.DEFAULT_GENERATE_PARAMETERIZED_FIELDS_RESOLVERS);
}
if (mappingConfig.getGenerateRequests()) {
// required for request serialization
mappingConfig.setGenerateToString(true);
}
}
......@@ -72,46 +88,48 @@ public class GraphqlCodegen {
long startTime = System.currentTimeMillis();
if (!schemas.isEmpty()) {
Document document = GraphqlDocumentParser.getDocument(schemas);
addScalarsToCustomMappingConfig(document);
initCustomTypeMappings(document);
processDocument(document);
}
long elapsed = System.currentTimeMillis() - startTime;
System.out.println(String.format("Finished processing %d schemas in %d ms", schemas.size(), elapsed));
System.out.println(String.format("Finished processing %d schema(s) in %d ms", schemas.size(), elapsed));
}
private void processDocument(Document document) throws IOException, TemplateException {
Set<String> typeNames = getAllTypeNames(document);
for (Definition<?> definition : document.getDefinitions()) {
GraphqlDefinitionType definitionType;
try {
definitionType = DefinitionTypeDeterminer.determine(definition);
} catch (UnsupportedGraphqlDefinitionException ex) {
continue;
}
switch (definitionType) {
case OPERATION:
generateOperation((ObjectTypeDefinition) definition);
break;
case TYPE:
generateType((ObjectTypeDefinition) definition, document);
generateFieldResolvers((ObjectTypeDefinition) definition);
break;
case INTERFACE:
generateInterface((InterfaceTypeDefinition) definition);
break;
case ENUM:
generateEnum((EnumTypeDefinition) definition);
break;
case INPUT:
generateInput((InputObjectTypeDefinition) definition);
break;
case UNION:
generateUnion((UnionTypeDefinition) definition);
processDefinition(document, definition, typeNames);
} catch (UnsupportedGraphqlDefinitionException ignored) {
}
}
System.out.println(String.format("Generated %d definitions in folder '%s'", document.getDefinitions().size(),
outputDir.getAbsolutePath()));
}
private void processDefinition(Document document, Definition<?> definition, Set<String> typeNames) throws IOException, TemplateException {
switch (DefinitionTypeDeterminer.determine(definition)) {
case OPERATION:
generateOperation((ObjectTypeDefinition) definition);
break;
case TYPE:
generateType((ObjectTypeDefinition) definition, document, typeNames);
generateFieldResolvers((ObjectTypeDefinition) definition);
break;
case INTERFACE:
generateInterface((InterfaceTypeDefinition) definition);
break;
case ENUM:
generateEnum((EnumTypeDefinition) definition);
break;
case INPUT:
generateInput((InputObjectTypeDefinition) definition);
break;
case UNION:
generateUnion((UnionTypeDefinition) definition);
}
}
private void generateUnion(UnionTypeDefinition definition) throws IOException, TemplateException {
Map<String, Object> dataModel = UnionDefinitionToDataModelMapper.map(mappingConfig, definition);
GraphqlCodegenFileCreator.generateFile(FreeMarkerTemplatesRegistry.unionTemplate, dataModel, outputDir);
......@@ -124,19 +142,32 @@ public class GraphqlCodegen {
private void generateOperation(ObjectTypeDefinition definition) throws IOException, TemplateException {
if (Boolean.TRUE.equals(mappingConfig.getGenerateApis())) {
for (FieldDefinition fieldDef : definition.getFieldDefinitions()) {
Map<String, Object> dataModel = FieldDefinitionToDataModelMapper.map(mappingConfig, fieldDef, definition.getName());
for (FieldDefinition operationDef : definition.getFieldDefinitions()) {
Map<String, Object> dataModel = FieldDefinitionToDataModelMapper.map(mappingConfig, operationDef, definition.getName());
GraphqlCodegenFileCreator.generateFile(FreeMarkerTemplatesRegistry.operationsTemplate, dataModel, outputDir);
}
// We need to generate a root object to workaround https://github.com/facebook/relay/issues/112
Map<String, Object> dataModel = ObjectDefinitionToDataModelMapper.map(mappingConfig, definition);
GraphqlCodegenFileCreator.generateFile(FreeMarkerTemplatesRegistry.operationsTemplate, dataModel, outputDir);
}
if (Boolean.TRUE.equals(mappingConfig.getGenerateRequests())) {
// generate request objects for graphql operations
for (FieldDefinition operationDef : definition.getFieldDefinitions()) {
Map<String, Object> requestDataModel = FieldDefinitionToRequestDataModelMapper.map(mappingConfig, operationDef, definition.getName());
GraphqlCodegenFileCreator.generateFile(FreeMarkerTemplatesRegistry.requestTemplate, requestDataModel, outputDir);
}
}
}
private void generateType(ObjectTypeDefinition definition, Document document) throws IOException, TemplateException {
private void generateType(ObjectTypeDefinition definition, Document document, Set<String> typeNames) throws IOException, TemplateException {
Map<String, Object> dataModel = TypeDefinitionToDataModelMapper.map(mappingConfig, definition, document);
GraphqlCodegenFileCreator.generateFile(FreeMarkerTemplatesRegistry.typeTemplate, dataModel, outputDir);
if (Boolean.TRUE.equals(mappingConfig.getGenerateRequests())) {
Map<String, Object> responseProjDataModel = TypeDefinitionToDataModelMapper.mapResponseProjection(mappingConfig, definition, document, typeNames);
GraphqlCodegenFileCreator.generateFile(FreeMarkerTemplatesRegistry.responseProjectionTemplate, responseProjDataModel, outputDir);
}
}
private void generateFieldResolvers(ObjectTypeDefinition definition) throws IOException, TemplateException {
......@@ -154,17 +185,30 @@ public class GraphqlCodegen {
GraphqlCodegenFileCreator.generateFile(FreeMarkerTemplatesRegistry.typeTemplate, dataModel, outputDir);
}
private static Set<String> getAllTypeNames(Document document) {
return document.getDefinitionsOfType(ObjectTypeDefinition.class)
.stream()
.filter(typeDef -> !Utils.isGraphqlOperation(typeDef.getName()))
.map(ObjectTypeDefinition::getName)
.collect(Collectors.toSet());
}
private void generateEnum(EnumTypeDefinition definition) throws IOException, TemplateException {
Map<String, Object> dataModel = EnumDefinitionToDataModelMapper.map(mappingConfig, definition);
GraphqlCodegenFileCreator.generateFile(FreeMarkerTemplatesRegistry.enumTemplate, dataModel, outputDir);
}
private void addScalarsToCustomMappingConfig(Document document) {
private void initCustomTypeMappings(Document document) {
for (Definition<?> definition : document.getDefinitions()) {
if (definition instanceof ScalarTypeDefinition) {
String scalarName = ((ScalarTypeDefinition) definition).getName();
mappingConfig.putCustomTypeMappingIfAbsent(scalarName, "String");
}
}
mappingConfig.putCustomTypeMappingIfAbsent("ID", "String");
mappingConfig.putCustomTypeMappingIfAbsent("String", "String");
mappingConfig.putCustomTypeMappingIfAbsent("Int", "Integer");
mappingConfig.putCustomTypeMappingIfAbsent("Float", "Double");
mappingConfig.putCustomTypeMappingIfAbsent("Boolean", "Boolean");
}
}
......@@ -43,9 +43,6 @@ public class EnumDefinitionToDataModelMapper {
* @return list of strings
*/
private static List<String> map(List<EnumValueDefinition> enumValueDefinitions) {
if (enumValueDefinitions == null) {
return Collections.emptyList();
}
return enumValueDefinitions.stream()
.map(EnumValueDefinition::getName)
.map(MapperUtils::capitalizeIfRestricted)
......
......@@ -2,12 +2,15 @@ package com.kobylynskyi.graphql.codegen.mapper;
import com.kobylynskyi.graphql.codegen.model.MappingConfig;
import com.kobylynskyi.graphql.codegen.model.ParameterDefinition;
import com.kobylynskyi.graphql.codegen.model.ProjectionParameterDefinition;
import com.kobylynskyi.graphql.codegen.utils.Utils;
import graphql.language.FieldDefinition;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.Set;
import static com.kobylynskyi.graphql.codegen.mapper.GraphqlTypeToJavaTypeMapper.*;
import static java.util.stream.Collectors.toList;
/**
* Mapper from GraphQL's FieldDefinition to a Freemarker-understandable format
......@@ -24,18 +27,74 @@ public class FieldDefinitionToParameterMapper {
* @param parentTypeName Name of the parent GraphQL type
* @return Freemarker data model of the GraphQL field definition
*/
public static List<ParameterDefinition> map(MappingConfig mappingConfig,
List<FieldDefinition> fieldDefinitions,
String parentTypeName) {
if (fieldDefinitions == null) {
return Collections.emptyList();
}
public static List<ParameterDefinition> mapFields(MappingConfig mappingConfig, List<FieldDefinition> fieldDefinitions,
String parentTypeName) {
return fieldDefinitions.stream()
.filter(fieldDef -> !generateResolversForField(mappingConfig, fieldDef, parentTypeName))
.map(fieldDef -> GraphqlTypeToJavaTypeMapper.map(mappingConfig, fieldDef, parentTypeName))
.collect(Collectors.toList());
.map(fieldDef -> mapField(mappingConfig, fieldDef, parentTypeName))
.collect(toList());
}
/**
* Map field definition to a Freemarker-understandable data model type
*
* @param mappingConfig Global mapping configuration
* @param fieldDefinitions List of GraphQL field definitions
* @param parentTypeName Name of the parent GraphQL type
* @param typeNames Names of all GraphQL types
* @return Freemarker data model of the GraphQL field definition
*/
public static List<ProjectionParameterDefinition> mapProjectionFields(MappingConfig mappingConfig,
List<FieldDefinition> fieldDefinitions,
String parentTypeName, Set<String> typeNames) {
return fieldDefinitions.stream()
.map(fieldDef -> mapProjectionField(mappingConfig, fieldDef, parentTypeName, typeNames))
.collect(toList());
}
/**
* Map GraphQL's FieldDefinition to a Freemarker-understandable format of parameter
*
* @param mappingConfig Global mapping configuration
* @param fieldDef GraphQL field definition
* @param parentTypeName Name of the parent type
* @return Freemarker-understandable format of parameter (field)
*/
private static ParameterDefinition mapField(MappingConfig mappingConfig, FieldDefinition fieldDef, String parentTypeName) {
ParameterDefinition parameter = new ParameterDefinition();
parameter.setName(MapperUtils.capitalizeIfRestricted(fieldDef.getName()));
parameter.setType(getJavaType(mappingConfig, fieldDef.getType(), fieldDef.getName(), parentTypeName));
parameter.setAnnotations(getAnnotations(mappingConfig, fieldDef.getType(), fieldDef.getName(), parentTypeName, false));
return parameter;
}
/**
* Map GraphQL's FieldDefinition to a Freemarker-understandable format of parameter
*
* @param mappingConfig Global mapping configuration
* @param fieldDef GraphQL field definition
* @param parentTypeName Name of the parent type
* @param typeNames Names of all GraphQL types
* @return Freemarker-understandable format of parameter (field)
*/
private static ProjectionParameterDefinition mapProjectionField(MappingConfig mappingConfig, FieldDefinition fieldDef, String parentTypeName, Set<String> typeNames) {
ProjectionParameterDefinition parameter = new ProjectionParameterDefinition();
parameter.setName(MapperUtils.capitalizeIfRestricted(fieldDef.getName()));
String nestedType = getNestedTypeName(fieldDef.getType());
if (typeNames.contains(nestedType)) {
parameter.setType(nestedType + mappingConfig.getResponseProjectionSuffix());
}
return parameter;
}
/**
* Check whether FieldResolver should be generated for a given field.
*
* @param mappingConfig Global mapping configuration
* @param fieldDef GraphQL field definition
* @param parentTypeName Name of the parent type
* @return <code>true</code> if FieldResolver will be generated for the field. <code>false</code> otherwise
*/
public static boolean generateResolversForField(MappingConfig mappingConfig,
FieldDefinition fieldDef,
String parentTypeName) {
......
package com.kobylynskyi.graphql.codegen.mapper;
import com.kobylynskyi.graphql.codegen.model.MappingConfig;
import com.kobylynskyi.graphql.codegen.utils.Utils;
import graphql.language.FieldDefinition;
import java.util.HashMap;
import java.util.Map;
import static com.kobylynskyi.graphql.codegen.model.DataModelFields.*;
/**
* Map field definition to a Request Freemarker data model
*
* @author kobylynskyi
*/
public class FieldDefinitionToRequestDataModelMapper {
/**
* Map field definition to a Request Freemarker data model.
*
* @param mappingConfig Global mapping configuration
* @param operationDef GraphQL operation definition
* @param objectTypeName Object type (e.g.: "Query", "Mutation" or "Subscription")
* @return Freemarker data model of the GraphQL request
*/
public static Map<String, Object> map(MappingConfig mappingConfig, FieldDefinition operationDef,
String objectTypeName) {
Map<String, Object> dataModel = new HashMap<>();
String packageName = MapperUtils.getModelPackageName(mappingConfig);
dataModel.put(PACKAGE, packageName);
dataModel.put(IMPORTS, MapperUtils.getImportsForRequests(mappingConfig, packageName));
dataModel.put(CLASS_NAME, getClassName(operationDef.getName(), objectTypeName, mappingConfig.getRequestSuffix()));
dataModel.put(OPERATION_NAME, operationDef.getName());
dataModel.put(OPERATION_TYPE, objectTypeName.toUpperCase());
dataModel.put(FIELDS, InputValueDefinitionToParameterMapper.map(mappingConfig, operationDef.getInputValueDefinitions(), operationDef.getName()));
dataModel.put(EQUALS_AND_HASH_CODE, mappingConfig.getGenerateEqualsAndHashCode());
dataModel.put(TO_STRING, mappingConfig.getGenerateToString());
return dataModel;
}
/**
* Examples:
* - EventsByCategoryQueryRequest
* - CreateEventMutationRequest
*/
private static String getClassName(String operationDefName, String objectType, String requestSuffix) {
if (Utils.isBlank(requestSuffix)) {
return Utils.capitalize(operationDefName) + objectType;
} else {
return Utils.capitalize(operationDefName) + objectType + requestSuffix;
}
}
}
package com.kobylynskyi.graphql.codegen.mapper;
import com.kobylynskyi.graphql.codegen.model.MappingConfig;
import com.kobylynskyi.graphql.codegen.model.ParameterDefinition;
import com.kobylynskyi.graphql.codegen.utils.Utils;
import graphql.language.*;
import graphql.language.ListType;
import graphql.language.NonNullType;
import graphql.language.Type;
import graphql.language.TypeName;
import java.util.ArrayList;
import java.util.List;
......@@ -18,39 +20,6 @@ import static graphql.language.OperationDefinition.Operation;
*/
class GraphqlTypeToJavaTypeMapper {
/**
* Map GraphQL's FieldDefinition to a Freemarker-understandable format of parameter
*
* @param mappingConfig Global mapping configuration
* @param fieldDef GraphQL field definition
* @param parentTypeName Name of the parent type
* @return Freemarker-understandable format of parameter (field)
*/
public static ParameterDefinition map(MappingConfig mappingConfig, FieldDefinition fieldDef, String parentTypeName) {
ParameterDefinition parameter = new ParameterDefinition();
parameter.setName(MapperUtils.capitalizeIfRestricted(fieldDef.getName()));
parameter.setType(getJavaType(mappingConfig, fieldDef.getType(), fieldDef.getName(), parentTypeName));
parameter.setAnnotations(getAnnotations(mappingConfig, fieldDef.getType(), fieldDef.getName(), parentTypeName, false));
return parameter;
}
/**
* Map GraphQL's InputValueDefinition to a Freemarker-understandable format of operation
*
* @param mappingConfig Global mapping configuration
* @param inputValueDefinition GraphQL input value definition
* @param parentTypeName Name of the parent type
* @return Freemarker-understandable format of parameter (field)
*/
public static ParameterDefinition map(MappingConfig mappingConfig, InputValueDefinition inputValueDefinition, String parentTypeName) {
ParameterDefinition parameter = new ParameterDefinition();
parameter.setName(MapperUtils.capitalizeIfRestricted(inputValueDefinition.getName()));
parameter.setType(getJavaType(mappingConfig, inputValueDefinition.getType()));
parameter.setDefaultValue(DefaultValueMapper.map(inputValueDefinition.getDefaultValue(), inputValueDefinition.getType()));
parameter.setAnnotations(getAnnotations(mappingConfig, inputValueDefinition.getType(), inputValueDefinition.getName(), parentTypeName));
return parameter;
}
/**
* Convert GraphQL type to a corresponding Java type
*
......@@ -83,6 +52,27 @@ class GraphqlTypeToJavaTypeMapper {
return null;
}
/**
* Get nested type of GraphQL Type. Example:
* Event -> Event
* Event! -> Event
* [Event!]! -> Event
* [[Event]] -> Event
*
* @param graphqlType GraphQL type
* @return GraphQL type without List/NonNull wrapping
*/
static String getNestedTypeName(Type graphqlType) {
if (graphqlType instanceof TypeName) {
return ((TypeName) graphqlType).getName();
} else if (graphqlType instanceof ListType) {
return getNestedTypeName(((ListType) graphqlType).getType());
} else if (graphqlType instanceof NonNullType) {
return getNestedTypeName(((NonNullType) graphqlType).getType());
}
return null;
}
/**
* Convert GraphQL type to a corresponding Java type
*
......@@ -99,20 +89,7 @@ class GraphqlTypeToJavaTypeMapper {
} else if (customTypesMapping.containsKey(graphlType)) {
return customTypesMapping.get(graphlType);
}
switch (graphlType) {
case "ID":
return "String";
case "Int":
return "Integer";
case "Float":
return "Double";
case "String":
case "Boolean":
return graphlType;
default:
// We need to refer other custom types/interfaces/unions with prefix and suffix
return MapperUtils.getClassNameWithPrefixAndSuffix(mappingConfig, graphlType);
}
return MapperUtils.getClassNameWithPrefixAndSuffix(mappingConfig, graphlType);
}
/**
......@@ -128,8 +105,8 @@ class GraphqlTypeToJavaTypeMapper {
return getAnnotations(mappingConfig, graphlType, name, parentTypeName, false);
}
private static List<String> getAnnotations(MappingConfig mappingConfig, Type type, String name, String parentTypeName,
boolean mandatory) {
static List<String> getAnnotations(MappingConfig mappingConfig, Type type, String name, String parentTypeName,
boolean mandatory) {
if (type instanceof TypeName) {
return getAnnotations(mappingConfig, ((TypeName) type).getName(), name, parentTypeName, mandatory);
} else if (type instanceof ListType) {
......@@ -205,7 +182,7 @@ class GraphqlTypeToJavaTypeMapper {
* @param parentTypeName Name of the parent type
* @return Java type wrapped into the subscriptionReturnType
*/
static String wrapIntoSubscriptionIfRequired(MappingConfig mappingConfig, String javaTypeName, String parentTypeName) {
private static String wrapIntoSubscriptionIfRequired(MappingConfig mappingConfig, String javaTypeName, String parentTypeName) {
if (parentTypeName.equalsIgnoreCase(Operation.SUBSCRIPTION.name())
&& !Utils.isBlank(mappingConfig.getSubscriptionReturnType())) {
return String.format("%s<%s>", mappingConfig.getSubscriptionReturnType(), javaTypeName);
......
......@@ -6,7 +6,10 @@ import graphql.language.InputValueDefinition;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import static com.kobylynskyi.graphql.codegen.mapper.GraphqlTypeToJavaTypeMapper.getAnnotations;
import static com.kobylynskyi.graphql.codegen.mapper.GraphqlTypeToJavaTypeMapper.getJavaType;
import static java.util.stream.Collectors.toList;
/**
* Mapper from GraphQL's InputValueDefinition to a Freemarker-understandable format
......@@ -24,12 +27,26 @@ public class InputValueDefinitionToParameterMapper {
* @return Freemarker data model of the GraphQL input value definition
*/
public static List<ParameterDefinition> map(MappingConfig mappingConfig, List<InputValueDefinition> valueDefinitions, String parentTypeName) {
if (valueDefinitions == null) {
return Collections.emptyList();
}
return valueDefinitions.stream()
.map(inputValueDefinition -> GraphqlTypeToJavaTypeMapper.map(mappingConfig, inputValueDefinition, parentTypeName))
.collect(Collectors.toList());
.map(inputValueDef -> map(mappingConfig, inputValueDef, parentTypeName))
.collect(toList());
}
/**
* Map GraphQL's InputValueDefinition to a Freemarker-understandable format of operation
*
* @param mappingConfig Global mapping configuration
* @param inputValueDefinition GraphQL input value definition
* @param parentTypeName Name of the parent type
* @return Freemarker-understandable format of parameter (field)
*/
private static ParameterDefinition map(MappingConfig mappingConfig, InputValueDefinition inputValueDefinition, String parentTypeName) {
ParameterDefinition parameter = new ParameterDefinition();
parameter.setName(MapperUtils.capitalizeIfRestricted(inputValueDefinition.getName()));
parameter.setType(getJavaType(mappingConfig, inputValueDefinition.getType()));
parameter.setDefaultValue(DefaultValueMapper.map(inputValueDefinition.getDefaultValue(), inputValueDefinition.getType()));
parameter.setAnnotations(getAnnotations(mappingConfig, inputValueDefinition.getType(), inputValueDefinition.getName(), parentTypeName));
return parameter;
}
}
......@@ -28,7 +28,7 @@ public class InterfaceDefinitionToDataModelMapper {
dataModel.put(PACKAGE, packageName);
dataModel.put(IMPORTS, MapperUtils.getImports(mappingConfig, packageName));
dataModel.put(CLASS_NAME, MapperUtils.getClassNameWithPrefixAndSuffix(mappingConfig, typeDef));
dataModel.put(FIELDS, FieldDefinitionToParameterMapper.map(mappingConfig, typeDef.getFieldDefinitions(), typeDef.getName()));
dataModel.put(FIELDS, FieldDefinitionToParameterMapper.mapFields(mappingConfig, typeDef.getFieldDefinitions(), typeDef.getName()));
return dataModel;
}
......
......@@ -192,6 +192,19 @@ public class MapperUtils {
return imports;
}
/**
* Returns imports required for the request class.
*
* @param mappingConfig Global mapping configuration
* @param packageName Package name of the generated class which will be ignored
* @return all imports required for a generated request class
*/
static Set<String> getImportsForRequests(MappingConfig mappingConfig, String packageName) {
Set<String> imports = getImports(mappingConfig, packageName);
imports.add("graphql.language");
return imports;
}
/**
* Determines if the specified operation is an async query or mutation
*
......
......@@ -2,6 +2,8 @@ package com.kobylynskyi.graphql.codegen.mapper;
import com.kobylynskyi.graphql.codegen.model.MappingConfig;
import com.kobylynskyi.graphql.codegen.model.ParameterDefinition;
import com.kobylynskyi.graphql.codegen.model.ProjectionParameterDefinition;
import com.kobylynskyi.graphql.codegen.utils.Utils;
import graphql.language.Document;
import graphql.language.InterfaceTypeDefinition;
import graphql.language.ObjectTypeDefinition;
......@@ -10,6 +12,7 @@ import java.util.*;
import java.util.stream.Collectors;
import static com.kobylynskyi.graphql.codegen.model.DataModelFields.*;
import static java.util.Collections.emptySet;
/**
* Map type definition to a Freemarker data model
......@@ -39,21 +42,79 @@ public class TypeDefinitionToDataModelMapper {
.map(anImplement -> GraphqlTypeToJavaTypeMapper.getJavaType(mappingConfig, anImplement))
.forEach(allInterfaces::add);
dataModel.put(IMPLEMENTS, allInterfaces);
// Merge attributes from the type and attributes from the interface
Set<ParameterDefinition> allParameters = new LinkedHashSet<>(FieldDefinitionToParameterMapper
.map(mappingConfig, typeDefinition.getFieldDefinitions(), typeDefinition.getName()));
List<InterfaceTypeDefinition> interfaces = getInterfacesOfType(mappingConfig, typeDefinition, document);
interfaces.stream()
.map(i -> FieldDefinitionToParameterMapper.map(mappingConfig, i.getFieldDefinitions(), i.getName()))
.forEach(allParameters::addAll);
dataModel.put(FIELDS, allParameters);
dataModel.put(FIELDS, getFields(mappingConfig, typeDefinition, document));
dataModel.put(EQUALS_AND_HASH_CODE, mappingConfig.getGenerateEqualsAndHashCode());
dataModel.put(TO_STRING, mappingConfig.getGenerateToString());
return dataModel;
}
/**
* Map type definition to a Freemarker data model of Response Projection.
*
* @param mappingConfig Global mapping configuration
* @param typeDefinition GraphQL type definition
* @param document Parent GraphQL document
* @param typeNames Names of all GraphQL types
* @return Freemarker data model of the GraphQL Response Projection
*/
public static Map<String, Object> mapResponseProjection(MappingConfig mappingConfig,
ObjectTypeDefinition typeDefinition, Document document,
Set<String> typeNames) {
Map<String, Object> dataModel = new HashMap<>();
String packageName = MapperUtils.getModelPackageName(mappingConfig);
dataModel.put(PACKAGE, packageName);
dataModel.put(IMPORTS, MapperUtils.getImports(mappingConfig, packageName));
dataModel.put(CLASS_NAME, Utils.capitalize(typeDefinition.getName()) + mappingConfig.getResponseProjectionSuffix());
dataModel.put(FIELDS, getProjectionFields(mappingConfig, typeDefinition, document, typeNames));
dataModel.put(EQUALS_AND_HASH_CODE, mappingConfig.getGenerateEqualsAndHashCode());
// dataModel.put(TO_STRING, mappingConfig.getGenerateToString()); already generated for serialization purposes
return dataModel;
}
/**
* Get merged attributes from the type and attributes from the interface.
*
* @param mappingConfig Global mapping configuration
* @param typeDefinition GraphQL type definition
* @param document Parent GraphQL document
* @return Freemarker data model of the GraphQL type
*/
@SuppressWarnings("CollectionAddAllCanBeReplacedWithConstructor")
private static Set<ParameterDefinition> getFields(MappingConfig mappingConfig, ObjectTypeDefinition typeDefinition,
Document document) {
Set<ParameterDefinition> allParameters = new LinkedHashSet<>();
allParameters.addAll(FieldDefinitionToParameterMapper.mapFields(mappingConfig,
typeDefinition.getFieldDefinitions(), typeDefinition.getName()));
List<InterfaceTypeDefinition> interfaces = getInterfacesOfType(mappingConfig, typeDefinition, document);
interfaces.stream().map(i -> FieldDefinitionToParameterMapper.mapFields(mappingConfig,
i.getFieldDefinitions(), i.getName()))
.forEach(allParameters::addAll);
return allParameters;
}
/**
* Get merged attributes from the type and attributes from the interface.
*
* @param mappingConfig Global mapping configuration
* @param typeDefinition GraphQL type definition
* @param document Parent GraphQL document
* @param typeNames Names of all GraphQL types
* @return Freemarker data model of the GraphQL type
*/
@SuppressWarnings("CollectionAddAllCanBeReplacedWithConstructor")
private static Set<ProjectionParameterDefinition> getProjectionFields(MappingConfig mappingConfig, ObjectTypeDefinition typeDefinition,
Document document, Set<String> typeNames) {
Set<ProjectionParameterDefinition> allParameters = new LinkedHashSet<>();
allParameters.addAll(FieldDefinitionToParameterMapper.mapProjectionFields(mappingConfig,
typeDefinition.getFieldDefinitions(), typeDefinition.getName(), typeNames));
List<InterfaceTypeDefinition> interfaces = getInterfacesOfType(mappingConfig, typeDefinition, document);
interfaces.stream().map(i -> FieldDefinitionToParameterMapper.mapProjectionFields(mappingConfig,
i.getFieldDefinitions(), i.getName(), typeNames))
.forEach(allParameters::addAll);
return allParameters;
}
/**
* Scan document and return all interfaces that given type implements.
*
......
......@@ -16,5 +16,7 @@ public final class DataModelFields {
public static final String OPERATIONS = "operations";
public static final String EQUALS_AND_HASH_CODE = "equalsAndHashCode";
public static final String TO_STRING = "toString";
public static final String OPERATION_TYPE = "operationType";
public static final String OPERATION_NAME = "operationName";
}
......@@ -3,6 +3,9 @@ package com.kobylynskyi.graphql.codegen.model;
public class DefaultMappingConfigValues {
public static final String DEFAULT_VALIDATION_ANNOTATION = "javax.validation.constraints.NotNull";
public static final boolean DEFAULT_GENERATE_REQUESTS = false;
public static final String DEFAULT_REQUEST_SUFFIX = "Request";
public static final String DEFAULT_RESPONSE_PROJECTION_SUFFIX = "ResponseProjection";
public static final boolean DEFAULT_GENERATE_APIS = true;
public static final boolean DEFAULT_EQUALS_AND_HASHCODE = false;
public static final boolean DEFAULT_TO_STRING = false;
......
......@@ -49,6 +49,11 @@ public class MappingConfig implements Combinable<MappingConfig> {
*/
private Set<String> fieldsWithResolvers = new HashSet<>();
// client-side codegen configs:
private Boolean generateRequests;
private String requestSuffix;
private String responseProjectionSuffix;
@Override
public void combine(MappingConfig source) {
if (source == null) {
......@@ -81,6 +86,9 @@ public class MappingConfig implements Combinable<MappingConfig> {
} else if (this.fieldsWithResolvers == null) {
this.fieldsWithResolvers = source.fieldsWithResolvers;
}
this.generateRequests = source.generateRequests != null ? source.generateRequests : this.generateRequests;
this.requestSuffix = source.requestSuffix != null ? source.requestSuffix : this.requestSuffix;
this.responseProjectionSuffix = source.responseProjectionSuffix != null ? source.responseProjectionSuffix : this.responseProjectionSuffix;
}
/**
......
package com.kobylynskyi.graphql.codegen.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* Freemarker-understandable format of parameter user in ResponseProjection
*
* @author kobylynskyi
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ProjectionParameterDefinition {
private String type;
private String name;
}
package com.kobylynskyi.graphql.codegen.model.request;
import graphql.language.OperationDefinition;
import java.util.Map;
public interface GraphQLOperationRequest {
OperationDefinition.Operation getOperationType();
String getOperationName();
Map<String, Object> getInput();
}
package com.kobylynskyi.graphql.codegen.model.request;
public class GraphQLRequest {
private final GraphQLOperationRequest request;
private final GraphQLResponseProjection responseProjection;
public GraphQLRequest(GraphQLOperationRequest request) {
this(request, null);
}
public GraphQLRequest(GraphQLOperationRequest request, GraphQLResponseProjection responseProjection) {
this.request = request;
this.responseProjection = responseProjection;
}
public GraphQLOperationRequest getRequest() {
return request;
}
public GraphQLResponseProjection getResponseProjection() {
return responseProjection;
}
@Override
public String toString() {
return GraphQLRequestSerializer.serialize(this);
}
}
package com.kobylynskyi.graphql.codegen.model.request;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.stream.Collectors;
public class GraphQLRequestSerializer {
public static String serialize(GraphQLRequest graphQLRequest) {
if (graphQLRequest == null || graphQLRequest.getRequest() == null) {
return null;
}
StringBuilder builder = new StringBuilder();
builder.append("{\"query\":\"");
builder.append(graphQLRequest.getRequest().getOperationType().name().toLowerCase());
builder.append(" { ");
builder.append(graphQLRequest.getRequest().getOperationName());
Map<String, Object> input = graphQLRequest.getRequest().getInput();
if (input != null && !input.isEmpty()) {
builder.append("(");
Iterator<Map.Entry<String, Object>> inputEntryIterator = input.entrySet().iterator();
while (inputEntryIterator.hasNext()) {
Map.Entry<String, Object> inputEntry = inputEntryIterator.next();
if (inputEntry.getValue() != null) {
builder.append(inputEntry.getKey());
builder.append(": ");
builder.append(getEntry(inputEntry.getValue()));
}
if (inputEntryIterator.hasNext()) {
builder.append(", ");
}
}
builder.append(")");
}
if (graphQLRequest.getResponseProjection() != null) {
builder.append(graphQLRequest.getResponseProjection().toString());
}
builder.append(" }");
builder.append("\"}");
return builder.toString();
}
private static String getEntry(Object input) {
if (input instanceof Collection) {
Collection<?> inputCollection = (Collection) input;
return inputCollection.stream()
.map(GraphQLRequestSerializer::getEntry)
.collect(Collectors.joining(", ", "[ ", " ]"));
}
if (input instanceof Enum<?>) {
return input.toString();
} else if (input instanceof String) {
return "\"" + input.toString() + "\"";
} else {
return input.toString();
}
}
}
package com.kobylynskyi.graphql.codegen.model.request;
public interface GraphQLResponseProjection {
}
......@@ -5,7 +5,7 @@ package ${package};
public enum ${className} {
<#list fields as field>
${field}<#if field_has_next>, </#if>
${field}<#if field_has_next>,</#if>
</#list>
}
\ No newline at end of file
<#if package?has_content>
package ${package};
</#if>
<#list imports as import>
import ${import}.*;
</#list>
public class ${className} implements com.kobylynskyi.graphql.codegen.model.request.GraphQLOperationRequest {
private static final OperationDefinition.Operation OPERATION_TYPE = OperationDefinition.Operation.${operationType};
private static final String OPERATION_NAME = "${operationName}";
private Map<String, Object> input = new LinkedHashMap<>();
public ${className}() {
}
<#list fields as field>
public void set${field.name?cap_first}(${field.type} ${field.name}) {
this.input.put("${field.name}", ${field.name});
}
</#list>
@Override
public OperationDefinition.Operation getOperationType() {
return OPERATION_TYPE;
}
@Override
public String getOperationName() {
return OPERATION_NAME;
}
@Override
public Map<String, Object> getInput() {
return input;
}
<#if equalsAndHashCode>
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final ${className} that = (${className}) obj;
return Objects.equals(getOperationType(), that.getOperationType()) &&
Objects.equals(getOperationName(), that.getOperationName()) &&
Objects.equals(input, that.input);
}
@Override
public int hashCode() {
return Objects.hash(getOperationType(), getOperationName(), input);
}
</#if>
<#if toString>
@Override
public String toString() {
return Objects.toString(input);
}
</#if>
}
<#if package?has_content>
package ${package};
</#if>
<#list imports as import>
import ${import}.*;
</#list>
public class ${className} implements com.kobylynskyi.graphql.codegen.model.request.GraphQLResponseProjection {
private Map<String, Object> fields = new LinkedHashMap<>();
public ${className}() {
}
<#list fields as field>
public ${className} ${field.name}(<#if field.type?has_content>${field.type} subProjection</#if>) {
fields.put("${field.name}", <#if field.type?has_content>subProjection<#else>null</#if>);
return this;
}
</#list>
<#if equalsAndHashCode>
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final ${className} that = (${className}) obj;
return Objects.equals(fields, that.fields);
}
@Override
public int hashCode() {
return Objects.hash(fields);
}
</#if>
@Override
public String toString() {
if (fields.isEmpty()) {
return "";
}
StringJoiner joiner = new StringJoiner(" ", "{ ", " }");
for (Map.Entry<String, Object> property : fields.entrySet()) {
joiner.add(property.getKey());
if (property.getValue() != null) {
joiner.add(" ").add(property.getValue().toString());
}
}
return joiner.toString();
}
}
......@@ -61,13 +61,19 @@ public class ${className} <#if implements?has_content>implements <#list implemen
<#if toString>
@Override
public String toString() {
return "${className}{"
StringJoiner joiner = new StringJoiner(", ", "{ ", " }");
<#if fields?has_content>
<#list fields as field>
+ "${field.name}='" + ${field.name} + "'<#if field_has_next>,</#if>"
if (${field.name} != null) {
<#if field.type == "String">
joiner.add("${field.name}: \"" + ${field.name} + "\"");
<#else>
joiner.add("${field.name}: " + ${field.name});
</#if>
}
</#list>
</#if>
+ "}";
return joiner.toString();
}
</#if>
}
package com.kobylynskyi.graphql.codegen;
import com.kobylynskyi.graphql.codegen.model.MappingConfig;
import com.kobylynskyi.graphql.codegen.utils.Utils;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Arrays;
import java.util.Objects;
import static com.kobylynskyi.graphql.codegen.TestUtils.assertSameTrimmedContent;
import static java.util.Collections.singletonList;
import static org.junit.jupiter.api.Assertions.assertNotNull;
class GraphqlCodegenRequestTest {
private final File outputBuildDir = new File("build/generated");
private final File outputJavaClassesDir = new File("build/generated/com/github/graphql");
private final MappingConfig mappingConfig = new MappingConfig();
@BeforeEach
void init() {
mappingConfig.setPackageName("com.github.graphql");
mappingConfig.setResponseProjectionSuffix("ResponseProjection");
mappingConfig.setRequestSuffix("Request");
mappingConfig.setGenerateRequests(true);
mappingConfig.setGenerateToString(false); // should be overridden to true
mappingConfig.setGenerateApis(false);
}
@AfterEach
void cleanup() throws IOException {
Utils.deleteDir(new File("build/generated"));
}
@Test
void generate_RequestAndResponseProjections() throws Exception {
new GraphqlCodegen(singletonList("src/test/resources/schemas/test.graphqls"),
outputBuildDir, mappingConfig).generate();
File[] files = Objects.requireNonNull(outputJavaClassesDir.listFiles());
assertSameTrimmedContent(new File("src/test/resources/expected-classes/request/EventResponseProjection.java.txt"),
getGeneratedFile(files, "EventResponseProjection.java"));
assertSameTrimmedContent(new File("src/test/resources/expected-classes/request/EventPropertyResponseProjection.java.txt"),
getGeneratedFile(files, "EventPropertyResponseProjection.java"));
assertSameTrimmedContent(new File("src/test/resources/expected-classes/request/EventsByCategoryAndStatusQueryRequest.java.txt"),
getGeneratedFile(files, "EventsByCategoryAndStatusQueryRequest.java"));
assertSameTrimmedContent(new File("src/test/resources/expected-classes/request/VersionQueryRequest.java.txt"),
getGeneratedFile(files, "VersionQueryRequest.java"));
assertSameTrimmedContent(new File("src/test/resources/expected-classes/request/EventsByIdsQueryRequest.java.txt"),
getGeneratedFile(files, "EventsByIdsQueryRequest.java"));
}
@Test
void generate_WithModelSuffix() throws Exception {
mappingConfig.setModelNameSuffix("TO");
new GraphqlCodegen(singletonList("src/test/resources/schemas/test.graphqls"),
outputBuildDir, mappingConfig).generate();
File[] files = Objects.requireNonNull(outputJavaClassesDir.listFiles());
assertSameTrimmedContent(new File("src/test/resources/expected-classes/request/EventStatusTO.java.txt"),
getGeneratedFile(files, "EventStatusTO.java"));
assertSameTrimmedContent(new File("src/test/resources/expected-classes/request/EventsByCategoryAndStatusQueryRequest_withModelSuffix.java.txt"),
getGeneratedFile(files, "EventsByCategoryAndStatusQueryRequest.java"));
}
@Test
void generate_RequestAndResponseProjections_github() throws Exception {
new GraphqlCodegen(singletonList("src/test/resources/schemas/github.graphqls"),
outputBuildDir, mappingConfig).generate();
File[] files = Objects.requireNonNull(outputJavaClassesDir.listFiles());
assertSameTrimmedContent(new File("src/test/resources/expected-classes/request/CodeOfConductResponseProjection.java.txt"),
getGeneratedFile(files, "CodeOfConductResponseProjection.java"));
assertSameTrimmedContent(new File("src/test/resources/expected-classes/request/UpdateRepositoryMutationRequest.java.txt"),
getGeneratedFile(files, "UpdateRepositoryMutationRequest.java"));
}
@Test
void generate_ToStringIsEnabledForInput() throws Exception {
new GraphqlCodegen(singletonList("src/test/resources/schemas/github.graphqls"),
outputBuildDir, mappingConfig).generate();
File[] files = Objects.requireNonNull(outputJavaClassesDir.listFiles());
assertSameTrimmedContent(new File("src/test/resources/expected-classes/request/AcceptTopicSuggestionInput.java.txt"),
getGeneratedFile(files, "AcceptTopicSuggestionInput.java"));
}
@Test
void generate_emptyRequestSuffix() throws Exception {
mappingConfig.setRequestSuffix("");
new GraphqlCodegen(singletonList("src/test/resources/schemas/test.graphqls"),
outputBuildDir, mappingConfig).generate();
File[] files = Objects.requireNonNull(outputJavaClassesDir.listFiles());
assertNotNull(getGeneratedFile(files, "EventsByCategoryAndStatusQuery.java"));
}
private static File getGeneratedFile(File[] files, String fileName) throws FileNotFoundException {
return Arrays.stream(files)
.filter(f -> f.getName().equalsIgnoreCase(fileName))
.findFirst()
.orElseThrow(FileNotFoundException::new);
}
}
\ No newline at end of file
......@@ -17,6 +17,7 @@ import java.nio.file.NoSuchFileException;
import java.util.*;
import static com.kobylynskyi.graphql.codegen.TestUtils.assertSameTrimmedContent;
import static java.util.Collections.singletonMap;
import static java.util.stream.Collectors.toList;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.*;
......@@ -48,11 +49,11 @@ class GraphqlCodegenTest {
File[] files = Objects.requireNonNull(outputJavaClassesDir.listFiles());
List<String> generatedFileNames = Arrays.stream(files).map(File::getName).sorted().collect(toList());
assertEquals(
Arrays.asList("CreateEventMutation.java", "Event.java", "EventByIdQuery.java",
"EventProperty.java", "EventStatus.java", "EventsByCategoryAndStatusQuery.java",
"EventsCreatedSubscription.java", "Mutation.java", "Query.java", "Subscription.java",
"VersionQuery.java"), generatedFileNames);
assertEquals(Arrays.asList(
"CreateEventMutation.java", "Event.java", "EventByIdQuery.java", "EventProperty.java",
"EventStatus.java", "EventsByCategoryAndStatusQuery.java", "EventsByIdsQuery.java",
"EventsCreatedSubscription.java", "Mutation.java", "Query.java", "Subscription.java",
"VersionQuery.java"), generatedFileNames);
for (File file : files) {
File expected = new File(String.format("src/test/resources/expected-classes/%s.txt", file.getName()));
......@@ -62,7 +63,7 @@ class GraphqlCodegenTest {
@Test
void generate_CustomMappings() throws Exception {
mappingConfig.setCustomTypesMapping(Collections.singletonMap("DateTime", "java.util.Date"));
mappingConfig.setCustomTypesMapping(new HashMap<>(singletonMap("DateTime", "java.util.Date")));
generator.generate();
......@@ -76,8 +77,7 @@ class GraphqlCodegenTest {
@Test
void generate_CustomMappings_Nested() throws Exception {
mappingConfig.setCustomTypesMapping(
new HashMap<>(Collections.singletonMap("EventProperty.intVal", "java.math.BigInteger")));
mappingConfig.setCustomTypesMapping(new HashMap<>(singletonMap("EventProperty.intVal", "java.math.BigInteger")));
generator.generate();
......@@ -121,9 +121,9 @@ class GraphqlCodegenTest {
@Test
void generate_CustomAnnotationMappings() throws Exception {
mappingConfig.setCustomTypesMapping(
new HashMap<>(Collections.singletonMap("Event.createdDateTime", "org.joda.time.DateTime")));
new HashMap<>(singletonMap("Event.createdDateTime", "org.joda.time.DateTime")));
mappingConfig.setCustomAnnotationsMapping(new HashMap<>(Collections.singletonMap("Event.createdDateTime",
mappingConfig.setCustomAnnotationsMapping(new HashMap<>(singletonMap("Event.createdDateTime",
"com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = com.example.json.DateTimeScalarDeserializer.class)")));
generator.generate();
......@@ -139,9 +139,9 @@ class GraphqlCodegenTest {
@Test
void generate_CustomAnnotationMappings_Type() throws Exception {
mappingConfig.setCustomTypesMapping(
new HashMap<>(Collections.singletonMap("DateTime", "org.joda.time.DateTime")));
new HashMap<>(singletonMap("DateTime", "org.joda.time.DateTime")));
mappingConfig.setCustomAnnotationsMapping(new HashMap<>(Collections.singletonMap("DateTime",
mappingConfig.setCustomAnnotationsMapping(new HashMap<>(singletonMap("DateTime",
"com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = com.example.json.DateTimeScalarDeserializer.class)")));
generator.generate();
......@@ -157,9 +157,9 @@ class GraphqlCodegenTest {
@Test
void generate_CustomAnnotationMappings_FieldType() throws Exception {
mappingConfig
.setCustomTypesMapping(new HashMap<>(Collections.singletonMap("DateTime", "org.joda.time.DateTime")));
.setCustomTypesMapping(new HashMap<>(singletonMap("DateTime", "org.joda.time.DateTime")));
mappingConfig.setCustomAnnotationsMapping(new HashMap<>(Collections.singletonMap("Event.createdDateTime",
mappingConfig.setCustomAnnotationsMapping(new HashMap<>(singletonMap("Event.createdDateTime",
"com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = com.example.json.DateTimeScalarDeserializer.class)")));
generator.generate();
......@@ -207,7 +207,7 @@ class GraphqlCodegenTest {
File[] apiFiles = Objects.requireNonNull(new File(outputJavaClassesDir, "api").listFiles());
List<String> generatedApiFileNames = Arrays.stream(apiFiles).map(File::getName).sorted().collect(toList());
assertEquals(Arrays.asList("CreateEventMutation.java", "EventByIdQuery.java",
"EventsByCategoryAndStatusQuery.java", "EventsCreatedSubscription.java", "Mutation.java", "Query.java",
"EventsByCategoryAndStatusQuery.java", "EventsByIdsQuery.java", "EventsCreatedSubscription.java", "Mutation.java", "Query.java",
"Subscription.java", "VersionQuery.java"), generatedApiFileNames);
Arrays.stream(apiFiles).forEach(file -> {
try {
......@@ -375,11 +375,11 @@ class GraphqlCodegenTest {
File[] files = Objects.requireNonNull(outputJavaClassesDir.listFiles());
assertFileContainsElements(files, "CreateEventMutation.java",
"import java.util.concurrent","CompletableFuture<Event> createEvent(");
"import java.util.concurrent", "CompletableFuture<Event> createEvent(");
}
private void assertFileContainsElements(File[] files, String fileName, String...elements)
private void assertFileContainsElements(File[] files, String fileName, String... elements)
throws IOException {
File file = getFile(files, fileName);
......
......@@ -46,6 +46,9 @@ class MappingConfigTest {
assertFalse(mappingConfig.getGenerateAsyncApi());
assertTrue(mappingConfig.getGenerateParameterizedFieldsResolvers());
assertEquals(singleton("5"), mappingConfig.getFieldsWithResolvers());
assertEquals("6", mappingConfig.getRequestSuffix());
assertEquals("7", mappingConfig.getResponseProjectionSuffix());
assertFalse(mappingConfig.getGenerateRequests());
}
@Test
......@@ -68,6 +71,9 @@ class MappingConfigTest {
assertFalse(mappingConfig.getGenerateAsyncApi());
assertTrue(mappingConfig.getGenerateParameterizedFieldsResolvers());
assertEquals(singleton("5"), mappingConfig.getFieldsWithResolvers());
assertEquals("6", mappingConfig.getRequestSuffix());
assertEquals("7", mappingConfig.getResponseProjectionSuffix());
assertFalse(mappingConfig.getGenerateRequests());
}
@Test
......@@ -92,6 +98,9 @@ class MappingConfigTest {
assertTrue(mappingConfig.getGenerateAsyncApi());
assertFalse(mappingConfig.getGenerateParameterizedFieldsResolvers());
assertEquals(new HashSet<>(Arrays.asList("5", "55")), mappingConfig.getFieldsWithResolvers());
assertEquals("66", mappingConfig.getRequestSuffix());
assertEquals("77", mappingConfig.getResponseProjectionSuffix());
assertTrue(mappingConfig.getGenerateRequests());
}
private static Map<String, String> hashMap(AbstractMap.SimpleEntry<String, String>... entries) {
......@@ -116,6 +125,9 @@ class MappingConfigTest {
config.setGenerateAsyncApi(false);
config.setGenerateParameterizedFieldsResolvers(true);
config.setFieldsWithResolvers(new HashSet<>(singletonList("5")));
config.setRequestSuffix("6");
config.setResponseProjectionSuffix("7");
config.setGenerateRequests(false);
return config;
}
......@@ -136,6 +148,9 @@ class MappingConfigTest {
config.setGenerateAsyncApi(true);
config.setGenerateParameterizedFieldsResolvers(false);
config.setFieldsWithResolvers(singleton("55"));
config.setRequestSuffix("66");
config.setResponseProjectionSuffix("77");
config.setGenerateRequests(true);
return config;
}
......
package com.kobylynskyi.graphql.codegen.model.request;
import com.kobylynskyi.graphql.codegen.model.request.data.*;
import com.kobylynskyi.graphql.codegen.utils.Utils;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
class GraphQLRequestSerializerTest {
@Test
void serialize_Null() {
assertNull(GraphQLRequestSerializer.serialize(null));
}
@Test
void serialize_Empty() {
assertNull(GraphQLRequestSerializer.serialize(new GraphQLRequest(null)));
}
@Test
void serialize_noResponseProjection() throws IOException {
String fileContent = getExpectedQueryString("versionQuery.txt");
GraphQLRequest graphQLRequest = new GraphQLRequest(new VersionQueryRequest());
String serializedQuery = graphQLRequest.toString().replaceAll(" +", " ").trim();
assertEquals(fileContent, serializedQuery);
}
@Test
void serialize_withResponseProjection() throws IOException {
String fileContent = getExpectedQueryString("eventsByCategoryAndStatusQuery.txt");
EventsByCategoryAndStatusQueryRequest request = new EventsByCategoryAndStatusQueryRequest();
request.setCategoryId("categoryIdValue1");
request.setStatus(Status.OPEN);
GraphQLRequest graphQLRequest = new GraphQLRequest(request,
new EventResponseProjection()
.id()
.active()
.properties(new EventPropertyResponseProjection()
.floatVal()
.child(new EventPropertyResponseProjection()
.intVal()
.parent(new EventResponseProjection()
.id()))
.booleanVal())
.status()
);
String serializedQuery = graphQLRequest.toString().replaceAll(" +", " ").trim();
assertEquals(fileContent, serializedQuery);
}
@Test
void serialize_complexRequestWithDefaultData() throws IOException {
String fileContent = getExpectedQueryString("updateIssueMutation.txt");
UpdateIssueMutationRequest requestWithDefaultData = new UpdateIssueMutationRequest();
requestWithDefaultData.setInput(new UpdateIssueInput());
GraphQLRequest graphQLRequest = new GraphQLRequest(requestWithDefaultData,
new UpdateIssuePayloadResponseProjection()
.clientMutationId()
.issue(new IssueResponseProjection()
.activeLockReason())
);
String serializedQuery = graphQLRequest.toString().replaceAll(" +", " ").trim();
assertEquals(fileContent, serializedQuery);
}
@Test
void serialize_collectionRequest() throws IOException {
String fileContent = getExpectedQueryString("eventsByIdsQuery.txt");
EventsByIdsQueryRequest request = new EventsByIdsQueryRequest();
request.setIds(Arrays.asList("4", "5", "6"));
GraphQLRequest graphQLRequest = new GraphQLRequest(request,
new EventResponseProjection()
.id()
);
String serializedQuery = graphQLRequest.toString().replaceAll(" +", " ").trim();
assertEquals(fileContent, serializedQuery);
}
private static String getExpectedQueryString(final String fileName) throws IOException {
String trimmedContent = Utils.getFileContent(
new File("src/test/resources/expected-classes/request/graphql-query/" + fileName).getPath())
.replaceAll(System.lineSeparator(), " ")
.replaceAll(" +", " ").trim();
return String.format("{\"query\":\"%s\"}", trimmedContent);
}
}
\ No newline at end of file
package com.kobylynskyi.graphql.codegen.model.request.data;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.StringJoiner;
public class EventPropertyResponseProjection implements com.kobylynskyi.graphql.codegen.model.request.GraphQLResponseProjection {
private Map<String, Object> fields = new LinkedHashMap<>();
public EventPropertyResponseProjection() {
}
public EventPropertyResponseProjection floatVal() {
fields.put("floatVal", null);
return this;
}
public EventPropertyResponseProjection booleanVal() {
fields.put("booleanVal", null);
return this;
}
public EventPropertyResponseProjection intVal() {
fields.put("intVal", null);
return this;
}
public EventPropertyResponseProjection stringVal() {
fields.put("stringVal", null);
return this;
}
public EventPropertyResponseProjection child(EventPropertyResponseProjection subProjection) {
fields.put("child", subProjection);
return this;
}
public EventPropertyResponseProjection parent(EventResponseProjection subProjection) {
fields.put("parent", subProjection);
return this;
}
@Override
public String toString() {
if (fields.isEmpty()) {
return "";
}
StringJoiner joiner = new StringJoiner(" ", "{ ", " }");
for (Map.Entry<String, Object> property : fields.entrySet()) {
joiner.add(property.getKey());
if (property.getValue() != null) {
joiner.add(" ").add(property.getValue().toString());
}
}
return joiner.toString();
}
}
\ No newline at end of file
package com.kobylynskyi.graphql.codegen.model.request.data;
import java.util.*;
public class EventResponseProjection implements com.kobylynskyi.graphql.codegen.model.request.GraphQLResponseProjection {
private Map<String, Object> fields = new LinkedHashMap<>();
public EventResponseProjection() {
}
public EventResponseProjection id() {
fields.put("id", null);
return this;
}
public EventResponseProjection categoryId() {
fields.put("categoryId", null);
return this;
}
public EventResponseProjection properties(EventPropertyResponseProjection subProjection) {
fields.put("properties", subProjection);
return this;
}
public EventResponseProjection status() {
fields.put("status", null);
return this;
}
public EventResponseProjection createdBy() {
fields.put("createdBy", null);
return this;
}
public EventResponseProjection createdDateTime() {
fields.put("createdDateTime", null);
return this;
}
public EventResponseProjection active() {
fields.put("active", null);
return this;
}
public EventResponseProjection rating() {
fields.put("rating", null);
return this;
}
@Override
public String toString() {
if (fields.isEmpty()) {
return "";
}
StringJoiner joiner = new StringJoiner(" ", "{ ", " }");
for (Map.Entry<String, Object> property : fields.entrySet()) {
joiner.add(property.getKey());
if (property.getValue() != null) {
joiner.add(" ").add(property.getValue().toString());
}
}
return joiner.toString();
}
}
\ No newline at end of file
package com.kobylynskyi.graphql.codegen.model.request.data;
import java.util.*;
import graphql.language.*;
public class EventsByCategoryAndStatusQueryRequest implements com.kobylynskyi.graphql.codegen.model.request.GraphQLOperationRequest {
private static final OperationDefinition.Operation OPERATION_TYPE = OperationDefinition.Operation.QUERY;
private static final String OPERATION_NAME = "eventsByCategoryAndStatus";
private Map<String, Object> input = new LinkedHashMap<>();
public EventsByCategoryAndStatusQueryRequest() {
}
public void setCategoryId(String categoryId) {
this.input.put("categoryId", categoryId);
}
public void setStatus(Status status) {
this.input.put("status", status);
}
@Override
public OperationDefinition.Operation getOperationType() {
return OPERATION_TYPE;
}
@Override
public String getOperationName() {
return OPERATION_NAME;
}
@Override
public Map<String, Object> getInput() {
return input;
}
}
\ No newline at end of file
package com.kobylynskyi.graphql.codegen.model.request.data;
import java.util.*;
import graphql.language.*;
public class EventsByIdsQueryRequest implements com.kobylynskyi.graphql.codegen.model.request.GraphQLOperationRequest {
private static final OperationDefinition.Operation OPERATION_TYPE = OperationDefinition.Operation.QUERY;
private static final String OPERATION_NAME = "eventsByIds";
private Map<String, Object> input = new LinkedHashMap<>();
public EventsByIdsQueryRequest() {
}
public void setIds(Collection<String> ids) {
this.input.put("ids", ids);
}
@Override
public OperationDefinition.Operation getOperationType() {
return OPERATION_TYPE;
}
@Override
public String getOperationName() {
return OPERATION_NAME;
}
@Override
public Map<String, Object> getInput() {
return input;
}
@Override
public String toString() {
return Objects.toString(input);
}
}
\ No newline at end of file
package com.kobylynskyi.graphql.codegen.model.request.data;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.StringJoiner;
public class IssueResponseProjection implements com.kobylynskyi.graphql.codegen.model.request.GraphQLResponseProjection {
private Map<String, Object> fields = new LinkedHashMap<>();
public IssueResponseProjection() {
}
public IssueResponseProjection activeLockReason() {
fields.put("activeLockReason", null);
return this;
}
// REST OF THE STUFF WAS REMOVED
@Override
public String toString() {
if (fields.isEmpty()) {
return "";
}
StringJoiner joiner = new StringJoiner(" ", "{ ", " }");
for (Map.Entry<String, Object> property : fields.entrySet()) {
joiner.add(property.getKey());
if (property.getValue() != null) {
joiner.add(" ").add(property.getValue().toString());
}
}
return joiner.toString();
}
}
package com.kobylynskyi.graphql.codegen.model.request.data;
public enum Status {
OPEN
}
package com.kobylynskyi.graphql.codegen.model.request.data;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.StringJoiner;
public class UpdateIssueInput {
private Double floatVal = 1.23;
private Boolean booleanVal = false;
private Integer intVal = 42;
private String stringVal = "my-default";
private Status enumVal = Status.OPEN;
private UpdateIssueInput objectWithNullDefault = null;
private Collection<Integer> intList = Arrays.asList(1, 2, 3);
private Collection<Integer> intListEmptyDefault = Collections.emptyList();
public UpdateIssueInput() {
}
@Override
public String toString() {
StringJoiner joiner = new StringJoiner(", ", "{ ", " }");
if (floatVal != null) {
joiner.add("floatVal: " + floatVal);
}
if (booleanVal != null) {
joiner.add("booleanVal: " + booleanVal);
}
if (intVal != null) {
joiner.add("intVal: " + intVal);
}
if (stringVal != null) {
joiner.add("stringVal: \"" + stringVal + "\"");
}
if (enumVal != null) {
joiner.add("enumVal: " + enumVal);
}
if (objectWithNullDefault != null) {
joiner.add("objectWithNullDefault: " + objectWithNullDefault);
}
if (intList != null) {
joiner.add("intList: " + intList);
}
if (intListEmptyDefault != null) {
joiner.add("intListEmptyDefault: " + intListEmptyDefault);
}
return joiner.toString();
}
}
package com.kobylynskyi.graphql.codegen.model.request.data;
import graphql.language.OperationDefinition;
import java.util.LinkedHashMap;
import java.util.Map;
public class UpdateIssueMutationRequest implements com.kobylynskyi.graphql.codegen.model.request.GraphQLOperationRequest {
private static final OperationDefinition.Operation OPERATION_TYPE = OperationDefinition.Operation.MUTATION;
private static final String OPERATION_NAME = "updateIssue";
private Map<String, Object> input = new LinkedHashMap<>();
public UpdateIssueMutationRequest() {
}
public void setInput(UpdateIssueInput input) {
this.input.put("input", input);
}
@Override
public OperationDefinition.Operation getOperationType() {
return OPERATION_TYPE;
}
@Override
public String getOperationName() {
return OPERATION_NAME;
}
@Override
public Map<String, Object> getInput() {
return input;
}
}
package com.kobylynskyi.graphql.codegen.model.request.data;
import java.util.*;
public class UpdateIssuePayloadResponseProjection implements com.kobylynskyi.graphql.codegen.model.request.GraphQLResponseProjection {
private Map<String, Object> fields = new LinkedHashMap<>();
public UpdateIssuePayloadResponseProjection() {
}
public UpdateIssuePayloadResponseProjection clientMutationId() {
fields.put("clientMutationId", null);
return this;
}
public UpdateIssuePayloadResponseProjection issue(IssueResponseProjection subProjection) {
fields.put("issue", subProjection);
return this;
}
@Override
public String toString() {
if (fields.isEmpty()) {
return "";
}
StringJoiner joiner = new StringJoiner(" ", "{ ", " }");
for (Map.Entry<String, Object> property : fields.entrySet()) {
joiner.add(property.getKey());
if (property.getValue() != null) {
joiner.add(" ").add(property.getValue().toString());
}
}
return joiner.toString();
}
}
package com.kobylynskyi.graphql.codegen.model.request.data;
import java.util.*;
import graphql.language.*;
public class VersionQueryRequest implements com.kobylynskyi.graphql.codegen.model.request.GraphQLOperationRequest {
private static final OperationDefinition.Operation OPERATION_TYPE = OperationDefinition.Operation.QUERY;
private static final String OPERATION_NAME = "version";
private Map<String, Object> input = new LinkedHashMap<>();
public VersionQueryRequest() {
}
@Override
public OperationDefinition.Operation getOperationType() {
return OPERATION_TYPE;
}
@Override
public String getOperationName() {
return OPERATION_NAME;
}
@Override
public Map<String, Object> getInput() {
return input;
}
}
\ No newline at end of file
......@@ -2,8 +2,8 @@ package com.kobylynskyi.graphql.test1;
public enum EventStatus {
OPEN,
IN_PROGRESS,
OPEN,
IN_PROGRESS,
LOGGED
}
\ No newline at end of file
package com.kobylynskyi.graphql.test1;
import java.util.*;
public interface EventsByIdsQuery {
@javax.validation.constraints.NotNull
Collection<Event> eventsByIds(Collection<String> ids) throws Exception;
}
\ No newline at end of file
......@@ -2,8 +2,8 @@ package com.kobylynskyi.graphql.testdefaults;
public enum MyEnum {
ONE,
TWO,
ONE,
TWO,
THREE
}
\ No newline at end of file
......@@ -13,4 +13,7 @@ public interface Query {
@javax.validation.constraints.NotNull
Event eventById(String id) throws Exception;
@javax.validation.constraints.NotNull
Collection<Event> eventsByIds(Collection<String> ids) throws Exception;
}
\ No newline at end of file
package com.github.graphql;
import java.util.*;
public class AcceptTopicSuggestionInput {
private String clientMutationId;
@javax.validation.constraints.NotNull
private String name;
@javax.validation.constraints.NotNull
private String repositoryId;
public AcceptTopicSuggestionInput() {
}
public AcceptTopicSuggestionInput(String clientMutationId, String name, String repositoryId) {
this.clientMutationId = clientMutationId;
this.name = name;
this.repositoryId = repositoryId;
}
public String getClientMutationId() {
return clientMutationId;
}
public void setClientMutationId(String clientMutationId) {
this.clientMutationId = clientMutationId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRepositoryId() {
return repositoryId;
}
public void setRepositoryId(String repositoryId) {
this.repositoryId = repositoryId;
}
@Override
public String toString() {
StringJoiner joiner = new StringJoiner(", ", "{ ", " }");
if (clientMutationId != null) {
joiner.add("clientMutationId: \"" + clientMutationId + "\"");
}
if (name != null) {
joiner.add("name: \"" + name + "\"");
}
if (repositoryId != null) {
joiner.add("repositoryId: \"" + repositoryId + "\"");
}
return joiner.toString();
}
}
\ No newline at end of file
package com.github.graphql;
import java.util.*;
public class CodeOfConductResponseProjection implements com.kobylynskyi.graphql.codegen.model.request.GraphQLResponseProjection {
private Map<String, Object> fields = new LinkedHashMap<>();
public CodeOfConductResponseProjection() {
}
public CodeOfConductResponseProjection body() {
fields.put("body", null);
return this;
}
public CodeOfConductResponseProjection id() {
fields.put("id", null);
return this;
}
public CodeOfConductResponseProjection key() {
fields.put("key", null);
return this;
}
public CodeOfConductResponseProjection name() {
fields.put("name", null);
return this;
}
public CodeOfConductResponseProjection resourcePath() {
fields.put("resourcePath", null);
return this;
}
public CodeOfConductResponseProjection url() {
fields.put("url", null);
return this;
}
@Override
public String toString() {
if (fields.isEmpty()) {
return "";
}
StringJoiner joiner = new StringJoiner(" ", "{ ", " }");
for (Map.Entry<String, Object> property : fields.entrySet()) {
joiner.add(property.getKey());
if (property.getValue() != null) {
joiner.add(" ").add(property.getValue().toString());
}
}
return joiner.toString();
}
}
\ No newline at end of file
package com.github.graphql;
import java.util.*;
public class EventPropertyResponseProjection implements com.kobylynskyi.graphql.codegen.model.request.GraphQLResponseProjection {
private Map<String, Object> fields = new LinkedHashMap<>();
public EventPropertyResponseProjection() {
}
public EventPropertyResponseProjection floatVal() {
fields.put("floatVal", null);
return this;
}
public EventPropertyResponseProjection booleanVal() {
fields.put("booleanVal", null);
return this;
}
public EventPropertyResponseProjection intVal() {
fields.put("intVal", null);
return this;
}
public EventPropertyResponseProjection stringVal() {
fields.put("stringVal", null);
return this;
}
public EventPropertyResponseProjection child(EventPropertyResponseProjection subProjection) {
fields.put("child", subProjection);
return this;
}
public EventPropertyResponseProjection parent(EventResponseProjection subProjection) {
fields.put("parent", subProjection);
return this;
}
@Override
public String toString() {
if (fields.isEmpty()) {
return "";
}
StringJoiner joiner = new StringJoiner(" ", "{ ", " }");
for (Map.Entry<String, Object> property : fields.entrySet()) {
joiner.add(property.getKey());
if (property.getValue() != null) {
joiner.add(" ").add(property.getValue().toString());
}
}
return joiner.toString();
}
}
\ No newline at end of file
package com.github.graphql;
import java.util.*;
public class EventResponseProjection implements com.kobylynskyi.graphql.codegen.model.request.GraphQLResponseProjection {
private Map<String, Object> fields = new LinkedHashMap<>();
public EventResponseProjection() {
}
public EventResponseProjection id() {
fields.put("id", null);
return this;
}
public EventResponseProjection categoryId() {
fields.put("categoryId", null);
return this;
}
public EventResponseProjection properties(EventPropertyResponseProjection subProjection) {
fields.put("properties", subProjection);
return this;
}
public EventResponseProjection status() {
fields.put("status", null);
return this;
}
public EventResponseProjection createdBy() {
fields.put("createdBy", null);
return this;
}
public EventResponseProjection createdDateTime() {
fields.put("createdDateTime", null);
return this;
}
public EventResponseProjection active() {
fields.put("active", null);
return this;
}
public EventResponseProjection rating() {
fields.put("rating", null);
return this;
}
@Override
public String toString() {
if (fields.isEmpty()) {
return "";
}
StringJoiner joiner = new StringJoiner(" ", "{ ", " }");
for (Map.Entry<String, Object> property : fields.entrySet()) {
joiner.add(property.getKey());
if (property.getValue() != null) {
joiner.add(" ").add(property.getValue().toString());
}
}
return joiner.toString();
}
}
\ No newline at end of file
package com.github.graphql;
public enum EventStatusTO {
OPEN,
IN_PROGRESS,
LOGGED
}
\ No newline at end of file
package com.github.graphql;
import java.util.*;
import graphql.language.*;
public class EventsByCategoryAndStatusQueryRequest implements com.kobylynskyi.graphql.codegen.model.request.GraphQLOperationRequest {
private static final OperationDefinition.Operation OPERATION_TYPE = OperationDefinition.Operation.QUERY;
private static final String OPERATION_NAME = "eventsByCategoryAndStatus";
private Map<String, Object> input = new LinkedHashMap<>();
public EventsByCategoryAndStatusQueryRequest() {
}
public void setCategoryId(String categoryId) {
this.input.put("categoryId", categoryId);
}
public void setStatus(EventStatus status) {
this.input.put("status", status);
}
@Override
public OperationDefinition.Operation getOperationType() {
return OPERATION_TYPE;
}
@Override
public String getOperationName() {
return OPERATION_NAME;
}
@Override
public Map<String, Object> getInput() {
return input;
}
@Override
public String toString() {
return Objects.toString(input);
}
}
\ No newline at end of file
package com.github.graphql;
import java.util.*;
import graphql.language.*;
public class EventsByCategoryAndStatusQueryRequest implements com.kobylynskyi.graphql.codegen.model.request.GraphQLOperationRequest {
private static final OperationDefinition.Operation OPERATION_TYPE = OperationDefinition.Operation.QUERY;
private static final String OPERATION_NAME = "eventsByCategoryAndStatus";
private Map<String, Object> input = new LinkedHashMap<>();
public EventsByCategoryAndStatusQueryRequest() {
}
public void setCategoryId(String categoryId) {
this.input.put("categoryId", categoryId);
}
public void setStatus(EventStatusTO status) {
this.input.put("status", status);
}
@Override
public OperationDefinition.Operation getOperationType() {
return OPERATION_TYPE;
}
@Override
public String getOperationName() {
return OPERATION_NAME;
}
@Override
public Map<String, Object> getInput() {
return input;
}
@Override
public String toString() {
return Objects.toString(input);
}
}
\ No newline at end of file
package com.github.graphql;
import java.util.*;
import graphql.language.*;
public class EventsByIdsQueryRequest implements com.kobylynskyi.graphql.codegen.model.request.GraphQLOperationRequest {
private static final OperationDefinition.Operation OPERATION_TYPE = OperationDefinition.Operation.QUERY;
private static final String OPERATION_NAME = "eventsByIds";
private Map<String, Object> input = new LinkedHashMap<>();
public EventsByIdsQueryRequest() {
}
public void setIds(Collection<String> ids) {
this.input.put("ids", ids);
}
@Override
public OperationDefinition.Operation getOperationType() {
return OPERATION_TYPE;
}
@Override
public String getOperationName() {
return OPERATION_NAME;
}
@Override
public Map<String, Object> getInput() {
return input;
}
@Override
public String toString() {
return Objects.toString(input);
}
}
\ No newline at end of file
package com.github.graphql;
import java.util.*;
import graphql.language.*;
public class UpdateRepositoryMutationRequest implements com.kobylynskyi.graphql.codegen.model.request.GraphQLOperationRequest {
private static final OperationDefinition.Operation OPERATION_TYPE = OperationDefinition.Operation.MUTATION;
private static final String OPERATION_NAME = "updateRepository";
private Map<String, Object> input = new LinkedHashMap<>();
public UpdateRepositoryMutationRequest() {
}
public void setInput(UpdateRepositoryInput input) {
this.input.put("input", input);
}
@Override
public OperationDefinition.Operation getOperationType() {
return OPERATION_TYPE;
}
@Override
public String getOperationName() {
return OPERATION_NAME;
}
@Override
public Map<String, Object> getInput() {
return input;
}
@Override
public String toString() {
return Objects.toString(input);
}
}
\ No newline at end of file
package com.github.graphql;
import java.util.*;
import graphql.language.*;
public class VersionQueryRequest implements com.kobylynskyi.graphql.codegen.model.request.GraphQLOperationRequest {
private static final OperationDefinition.Operation OPERATION_TYPE = OperationDefinition.Operation.QUERY;
private static final String OPERATION_NAME = "version";
private Map<String, Object> input = new LinkedHashMap<>();
public VersionQueryRequest() {
}
@Override
public OperationDefinition.Operation getOperationType() {
return OPERATION_TYPE;
}
@Override
public String getOperationName() {
return OPERATION_NAME;
}
@Override
public Map<String, Object> getInput() {
return input;
}
@Override
public String toString() {
return Objects.toString(input);
}
}
\ No newline at end of file
query {
eventsByCategoryAndStatus(categoryId: "categoryIdValue1", status: OPEN){
id
active
properties {
floatVal
child {
intVal
parent {
id
}
}
booleanVal
}
status
}
}
\ No newline at end of file
query {
eventsByIds(ids: [ "4", "5", "6" ]){
id
}
}
\ No newline at end of file
mutation {
updateIssue(input: {
floatVal: 1.23,
booleanVal: false,
intVal: 42,
stringVal: "my-default",
enumVal: OPEN,
intList: [1, 2, 3],
intListEmptyDefault: []
}){
clientMutationId
issue {
activeLockReason
}
}
}
......@@ -19,6 +19,9 @@ type Query {
# Single event by ID.
eventById(id: ID!): Event!
# Events by IDs.
eventsByIds(ids: [ID!]!): [Event!]!
}
type Mutation {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册