@Override public void validate(EndpointDefinition definition, DealiasingTypeVisitor dealiasingTypeVisitor) { definition.getArgs().stream() .filter(entry -> entry.getParamType().accept(ParameterTypeVisitor.IS_QUERY)) .forEach(argDefinition -> { boolean isValid = recursivelyValidate(argDefinition.getType(), dealiasingTypeVisitor); Preconditions.checkState(isValid, "Query parameters must be enums, primitives, aliases, list, sets " + "or optional of primitive: \"%s\" is not allowed", argDefinition.getArgName()); }); }
@Override public void validate(EndpointDefinition definition) { List<ArgumentDefinition> bodyParams = definition.getArgs() .stream() .filter(entry -> entry.getParamType().accept(ParameterTypeVisitor.IS_BODY)) .collect(Collectors.toList()); Preconditions.checkState(bodyParams.size() <= 1, "Endpoint cannot have multiple body parameters: %s", bodyParams.stream().map(e -> e.getArgName()).collect(Collectors.toList())); } }
private static List<ArgumentDefinition> parseArgs( Map<ParameterName, com.palantir.conjure.parser.services.ArgumentDefinition> args, HttpPath httpPath, ReferenceTypeResolver typeResolver) { ImmutableList.Builder<ArgumentDefinition> resultBuilder = ImmutableList.builder(); for (Map.Entry<com.palantir.conjure.parser.services.ParameterName, com.palantir.conjure.parser.services.ArgumentDefinition> entry : args.entrySet()) { com.palantir.conjure.parser.services.ArgumentDefinition original = entry.getValue(); ArgumentName argName = ArgumentName.of(entry.getKey().name()); ParameterType paramType = parseParameterType(original, argName, httpPath); ArgumentDefinition.Builder builder = ArgumentDefinition.builder() .argName(argName) .type(original.type().visit(new ConjureTypeParserVisitor(typeResolver))) .paramType(paramType) .docs(original.docs().map(Documentation::of)) .markers(parseMarkers(original.markers(), typeResolver)); resultBuilder.add(builder.build()); } return resultBuilder.build(); }
@Override public void validate(EndpointDefinition definition) { HttpMethod method = definition.getHttpMethod(); if (method.equals(HttpMethod.GET)) { boolean hasBody = definition.getArgs() .stream() .anyMatch(entry -> entry.getParamType().accept(ParameterTypeVisitor.IS_BODY)); Preconditions.checkState(!hasBody, "Endpoint cannot be a GET and contain a body: method: %s, path: %s", definition.getHttpMethod(), definition.getHttpPath()); } } }
@Override public void validate(EndpointDefinition definition) { definition.getArgs().forEach(arg -> { Matcher matcher = ANCHORED_PATTERN.matcher(arg.getArgName().get()); Preconditions.checkState(matcher.matches(), "Parameter names in endpoint paths and service definitions must match pattern %s: %s", ANCHORED_PATTERN, arg.getArgName().get()); }); } }
private static void validateServiceDefinition(ServiceDefinition serviceDef, Map<TypeName, TypeDefinition> definitionMap) { serviceDef.getEndpoints().stream().forEach(endpoint -> { endpoint.getArgs().stream() .filter(arg -> recursivelyFindNestedOptionals(arg.getType(), definitionMap, false)) .findAny() .ifPresent(arg -> { throw new IllegalStateException( "Illegal nested optionals found in one of the arguments of endpoint " + endpoint.getEndpointName().get()); }); endpoint.getReturns().ifPresent(returnType -> { if (recursivelyFindNestedOptionals(returnType, definitionMap, false)) { throw new IllegalStateException( "Illegal nested optionals found in return type of endpoint " + endpoint.getEndpointName().get()); } }); }); }
@Test public void testArgumentTypeValidator() { EndpointDefinition.Builder definition = EndpointDefinition.builder() .args(ImmutableList.of(ArgumentDefinition.builder() .argName(ArgumentName.of("testArg")) .type(Type.primitive(PrimitiveType.BINARY)) .paramType(ParameterType.header(HeaderParameterType.of(ParameterId.of("testArg")))) .build())) .endpointName(ENDPOINT_NAME) .httpMethod(HttpMethod.GET) .httpPath(HttpPath.of("/a/path")); assertThatThrownBy(() -> EndpointDefinitionValidator.validateAll(definition.build(), emptyDealiasingVisitor)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("Non body parameters cannot be of the 'binary' type: 'testArg' is not allowed"); }
@Override public void validate(EndpointDefinition definition) { Set<ArgumentName> pathParamIds = new HashSet<>(); definition.getArgs().stream() .filter(entry -> entry.getParamType().accept(ParameterTypeVisitor.IS_PATH)) .forEach(entry -> { boolean added = pathParamIds.add(entry.getArgName()); Preconditions.checkState(added, "Path parameter with identifier \"%s\" is defined multiple times for endpoint", entry.getArgName().get()); }); Set<ArgumentName> pathArgs = HttpPathValidator.pathArgs(definition.getHttpPath().get()); Set<ArgumentName> extraParams = Sets.difference(pathParamIds, pathArgs); Preconditions.checkState(extraParams.isEmpty(), "Path parameters defined in endpoint but not present in path template: %s. " + "Note that the `param-id` is no longer supported and the path template name is always " + "used instead. So make sure the path template name matches the path parameter defined " + "in endpoint.", extraParams); Set<ArgumentName> missingParams = Sets.difference(pathArgs, pathParamIds); Preconditions.checkState(missingParams.isEmpty(), "Path parameters defined path template but not present in endpoint: %s", missingParams); } }
@Override public void validate(EndpointDefinition definition) { definition.getArgs().forEach(arg -> { final Pattern pattern; ParameterType paramType = arg.getParamType(); if (paramType.accept(ParameterTypeVisitor.IS_BODY) || paramType.accept(ParameterTypeVisitor.IS_PATH) || paramType.accept(ParameterTypeVisitor.IS_QUERY)) { pattern = ANCHORED_PATTERN; } else if (paramType.accept(ParameterTypeVisitor.IS_HEADER)) { pattern = HEADER_PATTERN; } else { throw new IllegalStateException("Validation for paramType does not exist: " + arg.getParamType()); } if (paramType.accept(ParameterTypeVisitor.IS_QUERY)) { ParameterId paramId = paramType.accept(ParameterTypeVisitor.QUERY).getParamId(); Preconditions.checkState(pattern.matcher(paramId.get()).matches(), "Parameter ids with type %s must match pattern %s: %s", arg.getParamType(), pattern, paramId.get()); } else if (paramType.accept(ParameterTypeVisitor.IS_HEADER)) { ParameterId paramId = paramType.accept(ParameterTypeVisitor.HEADER).getParamId(); Preconditions.checkState(pattern.matcher(paramId.get()).matches(), "Parameter ids with type %s must match pattern %s: %s", arg.getParamType(), pattern, paramId.get()); } }); } }
@Override public void validate(EndpointDefinition definition, DealiasingTypeVisitor dealiasingTypeVisitor) { definition.getArgs().stream() .filter(entry -> entry.getParamType().accept(ParameterTypeVisitor.IS_HEADER)) .forEach(headerArgDefinition -> { boolean isValid = recursivelyValidate(headerArgDefinition.getType(), dealiasingTypeVisitor); Preconditions.checkState(isValid, "Header parameters must be enums, primitives, aliases or optional primitive:" + " \"%s\" is not allowed", headerArgDefinition.getArgName()); }); }
@Test public void testPathParamValidatorUniquePathParams() { ArgumentDefinition paramDefinition1 = ArgumentDefinition.builder() .argName(ArgumentName.of("paramName")) .type(Type.primitive(PrimitiveType.STRING)) .paramType(ParameterType.path(PathParameterType.of())) .build(); ArgumentDefinition paramDefinition2 = ArgumentDefinition.builder() .argName(ArgumentName.of("paramName")) .type(Type.primitive(PrimitiveType.STRING)) .paramType(ParameterType.path(PathParameterType.of())) .build(); EndpointDefinition.Builder definition = EndpointDefinition.builder() .args(ImmutableList.of(paramDefinition1, paramDefinition2)) .endpointName(ENDPOINT_NAME) .httpMethod(HttpMethod.GET) .httpPath(HttpPath.of("/a/path")); assertThatThrownBy(() -> EndpointDefinitionValidator.validateAll(definition.build(), emptyDealiasingVisitor)) .isInstanceOf(IllegalStateException.class) .hasMessage("Path parameter with identifier \"paramName\" is defined multiple times for endpoint"); }
@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()); }); } }
@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(EndpointDefinition definition, DealiasingTypeVisitor dealiasingTypeVisitor) { definition.getArgs() .stream() .filter(arg -> !arg.getParamType().accept(ParameterTypeVisitor.IS_BODY)) .forEach(arg -> { boolean isValid = dealiasingTypeVisitor.dealias(arg.getType()) .fold( typeDefinition -> true, type -> !type.accept(TypeVisitor.IS_BINARY) && !type.accept(TypeVisitor.IS_ANY) ); Preconditions.checkArgument( isValid, "Non body parameters cannot be of the 'binary' type: '%s' is not allowed", arg.getArgName()); }); } }
private EndpointDefinition.Builder createEndpoint(String paramName) { ArgumentDefinition arg = ArgumentDefinition.builder() .paramType(ParameterType.body(BodyParameterType.of())) .type(Type.primitive(PrimitiveType.STRING)) .argName(ArgumentName.of(paramName)) .build(); return EndpointDefinition.builder() .httpMethod(HttpMethod.POST) .httpPath(HttpPath.of("/a/path")) .args(ImmutableList.of(arg)) .endpointName(EndpointName.of("test")); } }
@Override public void validate(EndpointDefinition definition, DealiasingTypeVisitor dealiasingTypeVisitor) { definition.getArgs().stream() .filter(entry -> entry.getParamType().accept(ParameterTypeVisitor.IS_PATH) || entry.getParamType().accept(ParameterTypeVisitor.IS_QUERY)) .forEach(entry -> { Either<TypeDefinition, Type> conjureType = dealiasingTypeVisitor.dealias(entry.getType()); boolean isValid = conjureType.fold( typeDefinition -> true, type -> !type.accept(TypeVisitor.IS_PRIMITIVE) || type.accept(TypeVisitor.PRIMITIVE).get() != PrimitiveType.Value.BEARERTOKEN ); Preconditions.checkState(isValid, "Path or query parameters of type 'bearertoken' are not allowed as this " + "would introduce a security vulnerability: \"%s\"", entry.getArgName()); }); } }
@Test public void testComplexHeader() { EndpointDefinition.Builder definition = EndpointDefinition.builder() .args(ArgumentDefinition.builder() .argName(ArgumentName.of("someName")) .type(Type.list(ListType.builder().itemType(Type.primitive(PrimitiveType.STRING)).build())) .paramType(ParameterType.header(HeaderParameterType.of(ParameterId.of("someId")))) .build()) .endpointName(ENDPOINT_NAME) .httpMethod(HttpMethod.GET) .httpPath(HttpPath.of("/a/path")); assertThatThrownBy(() -> EndpointDefinitionValidator.validateAll(definition.build(), emptyDealiasingVisitor)) .isInstanceOf(IllegalStateException.class) .hasMessage("Header parameters must be enums, primitives, aliases or optional primitive:" + " \"someName\" is not allowed"); }
@Test public void testPathParamValidatorExtraParams() { ArgumentDefinition paramDefinition = ArgumentDefinition.builder() .type(Type.primitive(PrimitiveType.STRING)) .argName(ArgumentName.of("paramName")) .paramType(ParameterType.path(PathParameterType.of())) .build(); EndpointDefinition.Builder definition = EndpointDefinition.builder() .args(paramDefinition) .endpointName(ENDPOINT_NAME) .httpMethod(HttpMethod.GET) .httpPath(HttpPath.of("/a/path")); assertThatThrownBy(() -> EndpointDefinitionValidator.validateAll(definition.build(), emptyDealiasingVisitor)) .isInstanceOf(IllegalStateException.class) .hasMessageContaining( "Path parameters defined in endpoint but not present in path template: [paramName]"); }
@Test @SuppressWarnings("CheckReturnValue") public void testArgumentBodyTypeValidator() { EndpointDefinition.Builder definition = EndpointDefinition.builder() .args(ImmutableList.of(ArgumentDefinition.builder() .argName(ArgumentName.of("testArg")) .type(Type.primitive(PrimitiveType.BINARY)) .paramType(ParameterType.body(BodyParameterType.of())) .build())) .endpointName(ENDPOINT_NAME) .httpMethod(HttpMethod.POST) .httpPath(HttpPath.of("/a/path")); // Should not throw exception EndpointDefinitionValidator.validateAll(definition.build(), emptyDealiasingVisitor); }
private EndpointDefinition createEndpoint(ParameterType paramType) { ArgumentDefinition arg = ArgumentDefinition.builder() .argName(PARAMETER_NAME) .paramType(paramType) .type(Type.primitive(PrimitiveType.INTEGER)) .build(); EndpointDefinition definition = EndpointDefinition.builder() .httpMethod(HttpMethod.POST) .httpPath(HttpPath.of("/a/path")) .args(arg) .endpointName(EndpointName.of("test")) .build(); EndpointDefinitionValidator.validateAll(definition, dealiasingVisitor); return definition; } }