private static void validateTypeDefinition(TypeDefinition typeDef, Map<TypeName, TypeDefinition> definitionMap) { typeDef.accept(new TypeDefinition.Visitor<Void>() { @Override public Void visitAlias(AliasDefinition value) {
public static TypeDefinition parseEnumType( TypeName name, com.palantir.conjure.parser.types.complex.EnumTypeDefinition def) { EnumDefinition enumType = EnumDefinition.builder() .typeName(name) .values(def.values().stream().map(ConjureParserUtils::parseEnumValue).collect(Collectors.toList())) .docs(def.docs().map(Documentation::of)) .build(); EnumDefinitionValidator.validateAll(enumType); return TypeDefinition.enum_(enumType); }
public static TypeDefinition parseObjectType( TypeName name, com.palantir.conjure.parser.types.complex.ObjectTypeDefinition def, ConjureTypeParserVisitor.ReferenceTypeResolver typeResolver) { ObjectDefinition objectType = ObjectDefinition.builder() .typeName(name) .fields(parseField(def.fields(), typeResolver)) .docs(def.docs().map(Documentation::of)) .build(); ObjectDefinitionValidator.validate(objectType); return TypeDefinition.object(objectType); }
public static TypeDefinition parseAliasType( TypeName name, com.palantir.conjure.parser.types.reference.AliasTypeDefinition def, ConjureTypeParserVisitor.ReferenceTypeResolver typeResolver) { return TypeDefinition.alias(AliasDefinition.builder() .typeName(name) .alias(def.alias().visit(new ConjureTypeParserVisitor(typeResolver))) .docs(def.docs().map(Documentation::of)) .build()); }
public static TypeDefinition parseUnionType( TypeName name, com.palantir.conjure.parser.types.complex.UnionTypeDefinition def, ConjureTypeParserVisitor.ReferenceTypeResolver typeResolver) { UnionDefinition unionType = UnionDefinition.builder() .typeName(name) .union(parseField(def.union(), typeResolver)) .docs(def.docs().map(Documentation::of)) .build(); UnionDefinitionValidator.validateAll(unionType); return TypeDefinition.union(unionType); }
@Test public void testNoSelfRecursiveType() { ConjureDefinition conjureDef = ConjureDefinition.builder() .version(1) .types(ImmutableList.of(TypeDefinition.object( ObjectDefinition.builder() .typeName(FOO) .fields(FieldDefinition.of(FieldName.of("self"), Type.reference(FOO), DOCS)) .build()))) .build(); assertThatThrownBy(() -> ConjureDefinitionValidator.NO_RECURSIVE_TYPES.validate(conjureDef)) .isInstanceOf(IllegalStateException.class) .hasMessage("Illegal recursive data type: Foo -> Foo"); }
@Override public Either<TypeDefinition, Type> visitReference(TypeName value) { TypeDefinition typeDefinition = objects.get(value); Preconditions.checkState( typeDefinition != null, "Referenced TypeDefinition not found in map of types for TypeName: %s", value); return typeDefinition.accept(new TypeDefinition.Visitor<Either<TypeDefinition, Type>>() { @Override public Either<TypeDefinition, Type> visitAlias(AliasDefinition value) { // Recursively visit target of alias return value.getAlias().accept(DealiasingTypeVisitor.this); } @Override public Either<TypeDefinition, Type> visitEnum(EnumDefinition value) { return Either.left(TypeDefinition.enum_(value)); } @Override public Either<TypeDefinition, Type> visitObject(ObjectDefinition value) { return Either.left(TypeDefinition.object(value)); } @Override public Either<TypeDefinition, Type> visitUnion(UnionDefinition value) { return Either.left(TypeDefinition.union(value)); } @Override public Either<TypeDefinition, Type> visitUnknown(String unknownType) { throw new IllegalStateException("Unsupported type: " + unknownType); } }); }
@Test public void testNoRecursiveCycleType() { ConjureDefinition conjureDef = ConjureDefinition.builder() .version(1) .types(ImmutableList.of( TypeDefinition.object( ObjectDefinition.builder() .typeName(FOO) .fields(field(FieldName.of("bar"), "Bar")) .build()), TypeDefinition.object( ObjectDefinition.builder() .typeName(BAR) .fields(field(FieldName.of("foo"), "Foo")) .build()))) .build(); assertThatThrownBy(() -> ConjureDefinitionValidator.NO_RECURSIVE_TYPES.validate(conjureDef)) .isInstanceOf(IllegalStateException.class) .hasMessageStartingWith("Illegal recursive data type: "); }
@Override public Either<TypeDefinition, Type> visitReference(TypeName value) { TypeDefinition typeDefinition = objects.get(value); Preconditions.checkState( typeDefinition != null, "Referenced TypeDefinition not found in map of types for TypeName: %s", value); return typeDefinition.accept(new TypeDefinition.Visitor<Either<TypeDefinition, Type>>() { @Override public Either<TypeDefinition, Type> visitAlias(AliasDefinition value) { // Recursively visit target of alias return value.getAlias().accept(DealiasingTypeVisitor.this); } @Override public Either<TypeDefinition, Type> visitEnum(EnumDefinition value) { return Either.left(TypeDefinition.enum_(value)); } @Override public Either<TypeDefinition, Type> visitObject(ObjectDefinition value) { return Either.left(TypeDefinition.object(value)); } @Override public Either<TypeDefinition, Type> visitUnion(UnionDefinition value) { return Either.left(TypeDefinition.union(value)); } @Override public Either<TypeDefinition, Type> visitUnknown(String unknownType) { throw new IllegalStateException("Unsupported type: " + unknownType); } }); }
@Test public void testComplexHeaderObject() { TypeName typeName = TypeName.of("SomeObject", "com.palantir.foo"); EndpointDefinition.Builder definition = EndpointDefinition.builder() .args(ArgumentDefinition.builder() .argName(ArgumentName.of("someName")) .type(Type.reference(typeName)) .paramType(ParameterType.header(HeaderParameterType.of(ParameterId.of("SomeId")))) .build()) .endpointName(ENDPOINT_NAME) .httpMethod(HttpMethod.GET) .httpPath(HttpPath.of("/a/path")); DealiasingTypeVisitor dealiasingVisitor = new DealiasingTypeVisitor(ImmutableMap.of( typeName, TypeDefinition.object(ObjectDefinition.of(typeName, ImmutableList.of(), Documentation.of(""))) )); assertThatThrownBy(() -> EndpointDefinitionValidator.validateAll(definition.build(), dealiasingVisitor)) .isInstanceOf(IllegalStateException.class) .hasMessage("Header parameters must be enums, primitives, aliases or optional primitive:" + " \"someName\" is not allowed"); } }
@Override public void validate(ConjureDefinition definition) { // create mapping from object type name -> names of reference types that are fields of that type Multimap<TypeName, TypeName> typeToRefFields = HashMultimap.create(); definition.getTypes().stream().forEach(type -> getReferenceType(type) .ifPresent(entry -> typeToRefFields.put( type.accept(TypeDefinitionVisitor.TYPE_NAME), entry))); for (TypeName name : typeToRefFields.keySet()) { verifyTypeHasNoRecursiveDefinitions(name, typeToRefFields, new ArrayList<>()); } }
@Test public void testRecursiveTypeOkInReference() { Type referenceType = Type.reference(FOO); TypeDefinition objectDefinition = TypeDefinition.object( ObjectDefinition.builder() .typeName(TypeName.of("Foo", "bar")) .addAllFields(ImmutableList.of( FieldDefinition.of(FieldName.of("selfOptional"), Type.optional(OptionalType.of(Type.reference(FOO))), DOCS), FieldDefinition.of(FieldName.of("selfMap"), Type.map(MapType.of(referenceType, referenceType)), DOCS), FieldDefinition.of(FieldName.of("selfSet"), Type.set(SetType.of(referenceType)), DOCS), FieldDefinition.of(FieldName.of("selfList"), Type.list(ListType.of(referenceType)), DOCS))) .build()); ConjureDefinition conjureDef = ConjureDefinition.builder() .version(1) .types(ImmutableList.of(objectDefinition)) .build(); ConjureDefinitionValidator.NO_RECURSIVE_TYPES.validate(conjureDef); }
private static Optional<TypeName> getReferenceType(TypeDefinition typeDef) { if (typeDef.accept(TypeDefinitionVisitor.IS_OBJECT)) { ObjectDefinition objectDef = typeDef.accept(TypeDefinitionVisitor.OBJECT); for (FieldDefinition currField : objectDef.getFields()) { Optional<TypeName> referenceType = resolveReferenceType(currField.getType()); if (referenceType.isPresent()) { return referenceType; } } } else if (typeDef.accept(TypeDefinitionVisitor.IS_ALIAS)) { AliasDefinition aliasDef = typeDef.accept(TypeDefinitionVisitor.ALIAS); return resolveReferenceType(aliasDef.getAlias()); } return Optional.empty(); }
static Map<TypeName, TypeDefinition> parseObjects( com.palantir.conjure.parser.types.TypesDefinition parsed, ConjureTypeParserVisitor.ReferenceTypeResolver typeResolver) { Optional<String> defaultPackage = parsed.definitions().defaultConjurePackage().map(ConjurePackage::name); // no need to use validator here since TypeDefinitionParserVisitor calls each TypeDefinition parser that // validates its type. return parsed.definitions().objects().entrySet().stream() .map(entry -> entry.getValue().visit( new TypeDefinitionParserVisitor(entry.getKey().name(), defaultPackage, typeResolver))) .collect(Collectors.toMap(td -> td.accept(TypeDefinitionVisitor.TYPE_NAME), td -> td)); }
@Override public void validate(ConjureDefinition definition) { // create mapping for resolving reference types during validation Map<TypeName, TypeDefinition> definitionMap = definition.getTypes().stream().collect( Collectors.toMap(entry -> entry.accept(TypeDefinitionVisitor.TYPE_NAME), entry -> entry)); definition.getTypes().stream().forEach(def -> validateTypeDefinition(def, definitionMap)); definition.getErrors().forEach(def -> validateErrorDefinition(def, definitionMap)); definition.getServices().forEach(def -> validateServiceDefinition(def, definitionMap)); }
@Override public void validate(ConjureDefinition definition) { Set<TypeName> seenNames = new HashSet<>(); definition.getTypes().forEach(typeDef -> verifyNameIsUnique(seenNames, typeDef.accept(TypeDefinitionVisitor.TYPE_NAME))); definition.getErrors().forEach(errorDef -> verifyNameIsUnique(seenNames, errorDef.getErrorName())); definition.getServices().forEach(serviceDef -> verifyNameIsUnique(seenNames, serviceDef.getServiceName())); }
@Override public void validate(EndpointDefinition definition, DealiasingTypeVisitor dealiasingTypeVisitor) { definition.getArgs().stream() .filter(entry -> entry.getParamType().accept(ParameterTypeVisitor.IS_PATH)) .forEach(entry -> { Either<TypeDefinition, Type> resolvedType = dealiasingTypeVisitor.dealias(entry.getType()); Boolean isValid = resolvedType.fold( typeDefinition -> typeDefinition.accept(TypeDefinitionVisitor.IS_ENUM), type -> type.accept(TypeVisitor.IS_PRIMITIVE)); Preconditions.checkState(isValid, "Path parameters must be primitives or aliases: \"%s\" is not allowed", entry.getArgName()); }); } }
private static boolean recursivelyFindNestedOptionals( Type type, Map<TypeName, TypeDefinition> definitionMap, boolean isOptionalSeen) { if (type.accept(TypeVisitor.IS_REFERENCE)) { TypeDefinition referenceDefinition = definitionMap.get(type.accept(TypeVisitor.REFERENCE)); // we only care about reference of alias type if (referenceDefinition != null && referenceDefinition.accept(TypeDefinitionVisitor.IS_ALIAS)) { AliasDefinition aliasDef = referenceDefinition.accept(TypeDefinitionVisitor.ALIAS); return recursivelyFindNestedOptionals(aliasDef.getAlias(), definitionMap, isOptionalSeen); } } else if (type.accept(TypeVisitor.IS_OPTIONAL)) { if (isOptionalSeen) { return true; } return recursivelyFindNestedOptionals(type.accept(TypeVisitor.OPTIONAL).getItemType(), definitionMap, true); } return false; } }
private static Boolean recursivelyValidate(Type type, DealiasingTypeVisitor visitor) { return visitor.dealias(type).fold( typeDefinition -> typeDefinition.accept(TypeDefinitionVisitor.IS_ENUM), subType -> subType.accept(new Type.Visitor<Boolean>() { @Override
private static Boolean recursivelyValidate(Type type, DealiasingTypeVisitor visitor) { return visitor.dealias(type).fold( typeDefinition -> typeDefinition.accept(TypeDefinitionVisitor.IS_ENUM), subType -> { boolean definedPrimitive = subType.accept(TypeVisitor.IS_PRIMITIVE); boolean optionalPrimitive = subType.accept(TypeVisitor.IS_OPTIONAL) && recursivelyValidate(subType.accept(TypeVisitor.OPTIONAL).getItemType(), visitor); return definedPrimitive || optionalPrimitive; }); } }