Skip to content

Commit

Permalink
fix #974: The Conjure parser supports a --strict flag
Browse files Browse the repository at this point in the history
  • Loading branch information
carterkozak committed Nov 8, 2021
1 parent f079508 commit c6cad38
Show file tree
Hide file tree
Showing 28 changed files with 355 additions and 167 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,16 @@ private Conjure() {}
* Deserializes {@link ConjureDefinition} from their YAML representations in the given files.
*/
public static ConjureDefinition parse(Collection<File> files) {
return parse(files, ConjureOptions.builder().strict(false).build());
}

/**
* Deserializes {@link ConjureDefinition} from their YAML representations in the given files, using the provided
* {@link ConjureOptions}.
*/
public static ConjureDefinition parse(Collection<File> files, ConjureOptions options) {
Map<String, AnnotatedConjureSourceFile> sourceFiles = ConjureParser.parseAnnotated(files);
ConjureDefinition ir = ConjureParserUtils.parseConjureDef(sourceFiles);
ConjureDefinition ir = ConjureParserUtils.parseConjureDef(sourceFiles, options);
return NormalizeDefinition.normalize(ir);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* (c) Copyright 2018 Palantir Technologies Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.palantir.conjure.defs;

import org.immutables.value.Value;

@ConjureImmutablesStyle
@Value.Immutable
public interface ConjureOptions {

boolean strict();

static Builder builder() {
return new Builder();
}

class Builder extends ImmutableConjureOptions.Builder {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ public static String parsePackageOrElseThrow(
public static ErrorDefinition parseErrorType(
TypeName name,
com.palantir.conjure.parser.types.complex.ErrorTypeDefinition def,
ConjureTypeParserVisitor.ReferenceTypeResolver typeResolver) {
ConjureTypeParserVisitor.ReferenceTypeResolver typeResolver,
ConjureOptions options) {
ErrorDefinition errorType = ErrorDefinition.builder()
.errorName(name)
.namespace(ErrorNamespace.of(def.namespace().name()))
Expand All @@ -126,50 +127,53 @@ public static ErrorDefinition parseErrorType(
.docs(def.docs().map(Documentation::of))
.build();

ErrorDefinitionValidator.validate(errorType);
ErrorDefinitionValidator.validate(errorType, options);
return errorType;
}

public static TypeDefinition parseEnumType(
TypeName name, com.palantir.conjure.parser.types.complex.EnumTypeDefinition def) {
TypeName name, com.palantir.conjure.parser.types.complex.EnumTypeDefinition def, ConjureOptions options) {

EnumDefinition enumType = EnumDefinition.builder()
.typeName(name)
.values(def.values().stream()
.map(ConjureParserUtils::parseEnumValue)
.map((com.palantir.conjure.parser.types.complex.EnumValueDefinition enumValueDef) ->
parseEnumValue(enumValueDef, options))
.collect(Collectors.toList()))
.docs(def.docs().map(Documentation::of))
.build();

EnumDefinitionValidator.validateAll(enumType);
EnumDefinitionValidator.validateAll(enumType, options);
return TypeDefinition.enum_(enumType);
}

public static TypeDefinition parseUnionType(
TypeName name,
com.palantir.conjure.parser.types.complex.UnionTypeDefinition def,
ConjureTypeParserVisitor.ReferenceTypeResolver typeResolver) {
ConjureTypeParserVisitor.ReferenceTypeResolver typeResolver,
ConjureOptions options) {
UnionDefinition unionType = UnionDefinition.builder()
.typeName(name)
.union(parseField(def.union(), typeResolver))
.docs(def.docs().map(Documentation::of))
.build();

UnionDefinitionValidator.validateAll(unionType);
UnionDefinitionValidator.validateAll(unionType, options);
return TypeDefinition.union(unionType);
}

public static TypeDefinition parseObjectType(
TypeName name,
com.palantir.conjure.parser.types.complex.ObjectTypeDefinition def,
ConjureTypeParserVisitor.ReferenceTypeResolver typeResolver) {
ConjureTypeParserVisitor.ReferenceTypeResolver typeResolver,
ConjureOptions options) {
ObjectDefinition objectType = ObjectDefinition.builder()
.typeName(name)
.fields(parseField(def.fields(), typeResolver))
.docs(def.docs().map(Documentation::of))
.build();

ObjectDefinitionValidator.validate(objectType);
ObjectDefinitionValidator.validate(objectType, options);
return TypeDefinition.object(objectType);
}

Expand Down Expand Up @@ -203,7 +207,14 @@ static ConjureDefinition parseConjureDef(Collection<AnnotatedConjureSourceFile>
.collect(Collectors.toMap(source -> source.sourceFile().getAbsolutePath(), Function.identity())));
}

@Deprecated
static ConjureDefinition parseConjureDef(Map<String, AnnotatedConjureSourceFile> annotatedParsedDefs) {
return parseConjureDef(
annotatedParsedDefs, ConjureOptions.builder().strict(false).build());
}

static ConjureDefinition parseConjureDef(
Map<String, AnnotatedConjureSourceFile> annotatedParsedDefs, ConjureOptions options) {
ImmutableList.Builder<ServiceDefinition> servicesBuilder = ImmutableList.builder();
ImmutableList.Builder<ErrorDefinition> errorsBuilder = ImmutableList.builder();
ImmutableList.Builder<TypeDefinition> typesBuilder = ImmutableList.builder();
Expand All @@ -217,9 +228,9 @@ static ConjureDefinition parseConjureDef(Map<String, AnnotatedConjureSourceFile>
parsed.types(), annotatedParsed.importProviders(), annotatedParsedDefs);

// Resolve objects first, so we can use them in service validations
Map<TypeName, TypeDefinition> objects = parseObjects(parsed.types(), typeResolver);
Map<TypeName, TypeDefinition> objects = parseObjects(parsed.types(), typeResolver, options);
Map<TypeName, TypeDefinition> importedObjects =
parseImportObjects(parsed.types().conjureImports(), annotatedParsedDefs);
parseImportObjects(parsed.types().conjureImports(), annotatedParsedDefs, options);
Map<TypeName, TypeDefinition> allObjects = new HashMap<>();
allObjects.putAll(objects);
allObjects.putAll(importedObjects);
Expand All @@ -231,11 +242,12 @@ static ConjureDefinition parseConjureDef(Map<String, AnnotatedConjureSourceFile>
service,
TypeName.of(serviceName.name(), parseConjurePackage(service.conjurePackage())),
typeResolver,
dealiasingVisitor));
dealiasingVisitor,
options));
});

typesBuilder.addAll(objects.values());
errorsBuilder.addAll(parseErrors(parsed.types().definitions(), typeResolver));
errorsBuilder.addAll(parseErrors(parsed.types().definitions(), typeResolver, options));
} catch (RuntimeException e) {
throw new ConjureRuntimeException(
String.format("Encountered error trying to parse file '%s'", annotatedParsed.sourceFile()), e);
Expand All @@ -249,22 +261,25 @@ static ConjureDefinition parseConjureDef(Map<String, AnnotatedConjureSourceFile>
.services(servicesBuilder.build())
.build();

ConjureDefinitionValidator.validateAll(definition);
ConjureDefinitionValidator.validateAll(definition, options);
return definition;
}

/*
* Recursively resolve all imported types
*/
private static Map<TypeName, TypeDefinition> parseImportObjects(
Map<Namespace, ConjureImports> conjureImports, Map<String, AnnotatedConjureSourceFile> externalTypes) {
return innerParseImportObjects(conjureImports, externalTypes, new HashSet<>());
Map<Namespace, ConjureImports> conjureImports,
Map<String, AnnotatedConjureSourceFile> externalTypes,
ConjureOptions options) {
return innerParseImportObjects(conjureImports, externalTypes, new HashSet<>(), options);
}

private static Map<TypeName, TypeDefinition> innerParseImportObjects(
Map<Namespace, ConjureImports> conjureImports,
Map<String, AnnotatedConjureSourceFile> externalTypes,
Set<String> loadedFiles) {
Set<String> loadedFiles,
ConjureOptions options) {
Map<TypeName, TypeDefinition> allDefinitions = new HashMap<>();
conjureImports.values().forEach(conjureImport -> {
String pathKey = conjureImport
Expand All @@ -289,9 +304,9 @@ private static Map<TypeName, TypeDefinition> innerParseImportObjects(
ReferenceTypeResolver importTypeResolver =
new ConjureTypeParserVisitor.ByParsedRepresentationTypeNameResolver(
conjureDef.types(), importProviders, externalTypes);
allDefinitions.putAll(parseObjects(conjureDef.types(), importTypeResolver));
allDefinitions.putAll(parseObjects(conjureDef.types(), importTypeResolver, options));
allDefinitions.putAll(
innerParseImportObjects(conjureDef.types().conjureImports(), externalTypes, loadedFiles));
innerParseImportObjects(conjureDef.types().conjureImports(), externalTypes, loadedFiles, options));
});

return allDefinitions;
Expand All @@ -301,7 +316,8 @@ static ServiceDefinition parseService(
com.palantir.conjure.parser.services.ServiceDefinition parsed,
TypeName serviceName,
ReferenceTypeResolver typeResolver,
DealiasingTypeVisitor dealiasingVisitor) {
DealiasingTypeVisitor dealiasingVisitor,
ConjureOptions options) {
List<EndpointDefinition> endpoints = new ArrayList<>();
parsed.endpoints()
.forEach((name, def) -> endpoints.add(ConjureParserUtils.parseEndpoint(
Expand All @@ -310,55 +326,60 @@ static ServiceDefinition parseService(
parsed.basePath(),
parseAuthType(parsed.defaultAuth()),
typeResolver,
dealiasingVisitor)));
dealiasingVisitor,
options)));
ServiceDefinition service = ServiceDefinition.builder()
.serviceName(serviceName)
.docs(parsed.docs().map(Documentation::of))
.addAllEndpoints(endpoints)
.build();

ServiceDefinitionValidator.validateAll(service);
ServiceDefinitionValidator.validateAll(service, options);
return service;
}

static Map<TypeName, TypeDefinition> parseObjects(
com.palantir.conjure.parser.types.TypesDefinition parsed,
ConjureTypeParserVisitor.ReferenceTypeResolver typeResolver) {
ConjureTypeParserVisitor.ReferenceTypeResolver typeResolver,
ConjureOptions options) {
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)))
.visit(new TypeDefinitionParserVisitor(
entry.getKey().name(), defaultPackage, typeResolver, options)))
.collect(Collectors.toMap(td -> td.accept(TypeDefinitionVisitor.TYPE_NAME), td -> td));
}

static List<ErrorDefinition> parseErrors(
NamedTypesDefinition defs, ConjureTypeParserVisitor.ReferenceTypeResolver typeResolver) {
NamedTypesDefinition defs,
ConjureTypeParserVisitor.ReferenceTypeResolver typeResolver,
ConjureOptions options) {
Optional<String> defaultPackage = defs.defaultConjurePackage().map(ConjurePackage::name);
ImmutableList.Builder<ErrorDefinition> errorsBuidler = ImmutableList.builder();
errorsBuidler.addAll(defs.errors().entrySet().stream()
.map(entry -> {
TypeName typeName = TypeName.of(
entry.getKey().name(),
parsePackageOrElseThrow(entry.getValue().conjurePackage(), defaultPackage));
return parseErrorType(typeName, entry.getValue(), typeResolver);
return parseErrorType(typeName, entry.getValue(), typeResolver, options);
})
.collect(Collectors.toList()));
return errorsBuidler.build();
}

private static EnumValueDefinition parseEnumValue(
com.palantir.conjure.parser.types.complex.EnumValueDefinition def) {
com.palantir.conjure.parser.types.complex.EnumValueDefinition def, ConjureOptions options) {
EnumValueDefinition enumValue = EnumValueDefinition.builder()
.value(def.value())
.docs(def.docs().map(Documentation::of))
.deprecated(def.deprecated().map(Documentation::of))
.build();

EnumValueDefinitionValidator.validateAll(enumValue);
EnumValueDefinitionValidator.validateAll(enumValue, options);
return enumValue;
}

Expand Down Expand Up @@ -395,7 +416,8 @@ private static EndpointDefinition parseEndpoint(
PathString basePath,
Optional<AuthType> defaultAuth,
ReferenceTypeResolver typeResolver,
DealiasingTypeVisitor dealiasingVisitor) {
DealiasingTypeVisitor dealiasingVisitor,
ConjureOptions options) {

HttpPath httpPath = parseHttpPath(def, basePath);
EndpointDefinition endpoint = EndpointDefinition.builder()
Expand All @@ -413,7 +435,7 @@ private static EndpointDefinition parseEndpoint(
.deprecated(def.deprecated().map(Documentation::of))
.build();

EndpointDefinitionValidator.validateAll(endpoint, dealiasingVisitor);
EndpointDefinitionValidator.validateAll(endpoint, dealiasingVisitor, options);
return endpoint;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,17 @@ public final class TypeDefinitionParserVisitor implements TypeDefinitionVisitor<
private final String name;
private final Optional<String> defaultPackage;
private final ConjureTypeParserVisitor.ReferenceTypeResolver typeResolver;
private final ConjureOptions options;

public TypeDefinitionParserVisitor(
String typeName,
Optional<String> defaultPackage,
ConjureTypeParserVisitor.ReferenceTypeResolver typeResolver) {
ConjureTypeParserVisitor.ReferenceTypeResolver typeResolver,
ConjureOptions options) {
this.name = typeName;
this.defaultPackage = defaultPackage;
this.typeResolver = typeResolver;
this.options = options;
}

@Override
Expand All @@ -48,18 +51,19 @@ public TypeDefinition visit(AliasTypeDefinition def) {

@Override
public TypeDefinition visit(EnumTypeDefinition def) {
return ConjureParserUtils.parseEnumType(ConjureParserUtils.createTypeName(name, def, defaultPackage), def);
return ConjureParserUtils.parseEnumType(
ConjureParserUtils.createTypeName(name, def, defaultPackage), def, options);
}

@Override
public TypeDefinition visit(ObjectTypeDefinition def) {
return ConjureParserUtils.parseObjectType(
ConjureParserUtils.createTypeName(name, def, defaultPackage), def, typeResolver);
ConjureParserUtils.createTypeName(name, def, defaultPackage), def, typeResolver, options);
}

@Override
public TypeDefinition visit(UnionTypeDefinition def) {
return ConjureParserUtils.parseUnionType(
ConjureParserUtils.createTypeName(name, def, defaultPackage), def, typeResolver);
ConjureParserUtils.createTypeName(name, def, defaultPackage), def, typeResolver, options);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.palantir.conjure.defs.validator;

import com.palantir.conjure.defs.ConjureOptions;
import com.palantir.conjure.visitor.DealiasingTypeVisitor;

@com.google.errorprone.annotations.Immutable
Expand All @@ -24,5 +25,5 @@ public interface ConjureContextualValidator<T> {
* Validates that the provided definition is valid according to Conjure rules. Throws an exception if the
* provided definition is invalid.
*/
void validate(T definition, DealiasingTypeVisitor dealiasingTypeVisitor);
void validate(T definition, DealiasingTypeVisitor dealiasingTypeVisitor, ConjureOptions options);
}
Loading

0 comments on commit c6cad38

Please sign in to comment.